[pypy-commit] pypy default: merge small-unroll-improvements

cfbolz noreply at buildbot.pypy.org
Tue Apr 22 11:47:17 CEST 2014


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: 
Changeset: r70851:82141f03207d
Date: 2014-04-22 11:46 +0200
http://bitbucket.org/pypy/pypy/changeset/82141f03207d/

Log:	merge small-unroll-improvements

	a cleanup and generalization of unroll, in particularly the virtual
	state handling. reduces code duplication and various hacks. Fixes a
	few rare miscompiles. This also improves optimization by
	generalizing a few matching cases in the virtualstate matching.

diff too long, truncating to 2000 out of 2662 lines

diff --git a/pypy/doc/whatsnew-2.3.0.rst b/pypy/doc/whatsnew-2.3.0.rst
--- a/pypy/doc/whatsnew-2.3.0.rst
+++ b/pypy/doc/whatsnew-2.3.0.rst
@@ -149,3 +149,6 @@
 
 .. branch: openbsd-lib-prefix
 add 'lib' prefix to link libraries on OpenBSD
+
+.. branch: small-unroll-improvements
+Improve optimiziation of small allocation-heavy loops in the JIT
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -5,3 +5,7 @@
 .. this is a revision shortly after release-2.3.x
 .. startrev: ba569fe1efdb
 
+
+
+.. branch: small-unroll-improvements
+Improve optimiziation of small allocation-heavy loops in the JIT
diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -106,7 +106,7 @@
 
 def compile_loop(metainterp, greenkey, start,
                  inputargs, jumpargs,
-                 resume_at_jump_descr, full_preamble_needed=True,
+                 full_preamble_needed=True,
                  try_disabling_unroll=False):
     """Try to compile a new procedure by closing the current history back
     to the first operation.
@@ -128,7 +128,6 @@
     part = create_empty_loop(metainterp)
     part.inputargs = inputargs[:]
     h_ops = history.operations
-    part.resume_at_jump_descr = resume_at_jump_descr
     part.operations = [ResOperation(rop.LABEL, inputargs, None, descr=TargetToken(jitcell_token))] + \
                       [h_ops[i].clone() for i in range(start, len(h_ops))] + \
                       [ResOperation(rop.LABEL, jumpargs, None, descr=jitcell_token)]
@@ -187,7 +186,7 @@
 
 def compile_retrace(metainterp, greenkey, start,
                     inputargs, jumpargs,
-                    resume_at_jump_descr, partial_trace, resumekey):
+                    partial_trace, resumekey):
     """Try to compile a new procedure by closing the current history back
     to the first operation.
     """
@@ -203,7 +202,6 @@
 
     part = create_empty_loop(metainterp)
     part.inputargs = inputargs[:]
-    part.resume_at_jump_descr = resume_at_jump_descr
     h_ops = history.operations
 
     part.operations = [partial_trace.operations[-1]] + \
@@ -765,7 +763,7 @@
         metainterp_sd.stats.add_jitcell_token(jitcell_token)
 
 
-def compile_trace(metainterp, resumekey, resume_at_jump_descr=None):
+def compile_trace(metainterp, resumekey):
     """Try to compile a new bridge leading from the beginning of the history
     to some existing place.
     """
@@ -781,7 +779,6 @@
     # clone ops, as optimize_bridge can mutate the ops
 
     new_trace.operations = [op.clone() for op in metainterp.history.operations]
-    new_trace.resume_at_jump_descr = resume_at_jump_descr
     metainterp_sd = metainterp.staticdata
     state = metainterp.jitdriver_sd.warmstate
     if isinstance(resumekey, ResumeAtPositionDescr):
diff --git a/rpython/jit/metainterp/history.py b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -628,7 +628,6 @@
     call_pure_results = None
     logops = None
     quasi_immutable_deps = None
-    resume_at_jump_descr = None
 
     def _token(*args):
         raise Exception("TreeLoop.token is killed")
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -31,6 +31,12 @@
     def clone(self):
         return LenBound(self.mode, self.descr, self.bound.clone())
 
+    def generalization_of(self, other):
+        return (other is not None and
+                self.mode == other.mode and
+                self.descr == other.descr and
+                self.bound.contains_bound(other.bound))
+
 class OptValue(object):
     __metaclass__ = extendabletype
     _attrs_ = ('box', 'known_class', 'last_guard', 'level', 'intbound', 'lenbound')
@@ -129,13 +135,21 @@
     def force_at_end_of_preamble(self, already_forced, optforce):
         return self
 
-    def get_args_for_fail(self, modifier):
+    # visitor API
+
+    def visitor_walk_recursive(self, visitor):
         pass
 
-    def make_virtual_info(self, modifier, fieldnums):
-        #raise NotImplementedError # should not be called on this level
-        assert fieldnums is None
-        return modifier.make_not_virtual(self)
+    @specialize.argtype(1)
+    def visitor_dispatch_virtual_type(self, visitor):
+        if self.is_virtual():
+            return self._visitor_dispatch_virtual_type(visitor)
+        else:
+            return visitor.visit_not_virtual(self)
+
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
+        assert 0, "unreachable"
 
     def is_constant(self):
         return self.level == LEVEL_CONSTANT
diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py b/rpython/jit/metainterp/optimizeopt/rewrite.py
--- a/rpython/jit/metainterp/optimizeopt/rewrite.py
+++ b/rpython/jit/metainterp/optimizeopt/rewrite.py
@@ -543,6 +543,9 @@
             return
         self.emit_operation(op)
 
+    def optimize_GUARD_FUTURE_CONDITION(self, op):
+        pass # just remove it
+
     def optimize_INT_FLOORDIV(self, op):
         v1 = self.getvalue(op.getarg(0))
         v2 = self.getvalue(op.getarg(1))
diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py b/rpython/jit/metainterp/optimizeopt/simplify.py
--- a/rpython/jit/metainterp/optimizeopt/simplify.py
+++ b/rpython/jit/metainterp/optimizeopt/simplify.py
@@ -61,6 +61,9 @@
                 op.setdescr(descr.target_tokens[0])
         self.emit_operation(op)
 
+    def optimize_GUARD_FUTURE_CONDITION(self, op):
+        pass
+
 dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
         default=OptSimplify.emit_operation)
 OptSimplify.propagate_forward = dispatch_opt
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py b/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_multilabel.py
@@ -1,6 +1,6 @@
 from __future__ import with_statement
 from rpython.jit.metainterp.optimizeopt.test.test_util import (
-    LLtypeMixin, BaseTest, Storage, _sortboxes, FakeDescrWithSnapshot,
+    LLtypeMixin, BaseTest, Storage, _sortboxes,
     FakeMetaInterpStaticData)
 from rpython.jit.metainterp.history import TreeLoop, JitCellToken, TargetToken
 from rpython.jit.metainterp.resoperation import rop, opname, ResOperation
@@ -8,6 +8,8 @@
 from py.test import raises
 from rpython.jit.metainterp.optimizeopt.optimizer import Optimization
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
+from rpython.jit.metainterp.optimizeopt.heap import OptHeap
+from rpython.jit.metainterp.optimizeopt.rewrite import OptRewrite
 
 
 class BaseTestMultiLabel(BaseTest):
@@ -20,7 +22,6 @@
 
         part = TreeLoop('part')
         part.inputargs = loop.inputargs
-        part.resume_at_jump_descr = FakeDescrWithSnapshot()
         token = loop.original_jitcell_token
 
         optimized = TreeLoop('optimized')
@@ -42,6 +43,7 @@
                 operations.append(label)
             part.operations = operations
 
+            self.add_guard_future_condition(part)
             self._do_optimize_loop(part, None)
             if part.operations[-1].getopnum() == rop.LABEL:
                 last_label = [part.operations.pop()]
@@ -502,7 +504,7 @@
         self.loop = loop
         loop.call_pure_results = args_dict()
         metainterp_sd = FakeMetaInterpStaticData(self.cpu)
-        optimize_unroll(metainterp_sd, loop, [OptRenameStrlen(), OptPure()], True)
+        optimize_unroll(metainterp_sd, loop, [OptRewrite(), OptRenameStrlen(), OptHeap(), OptPure()], True)
 
     def test_optimizer_renaming_boxes1(self):
         ops = """
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebasic.py
@@ -61,24 +61,6 @@
     lst6 = virt1._get_field_descr_list()
     assert lst6 is lst3
 
-def test_reuse_vinfo():
-    class FakeVInfo(object):
-        def set_content(self, fieldnums):
-            self.fieldnums = fieldnums
-        def equals(self, fieldnums):
-            return self.fieldnums == fieldnums
-    class FakeVirtualValue(virtualize.AbstractVirtualValue):
-        def _make_virtual(self, *args):
-            return FakeVInfo()
-    v1 = FakeVirtualValue(None, None)
-    vinfo1 = v1.make_virtual_info(None, [1, 2, 4])
-    vinfo2 = v1.make_virtual_info(None, [1, 2, 4])
-    assert vinfo1 is vinfo2
-    vinfo3 = v1.make_virtual_info(None, [1, 2, 6])
-    assert vinfo3 is not vinfo2
-    vinfo4 = v1.make_virtual_info(None, [1, 2, 6])
-    assert vinfo3 is vinfo4
-
 def test_descrlist_dict():
     from rpython.jit.metainterp.optimizeopt import util as optimizeutil
     h1 = optimizeutil.descrlist_hash([])
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -51,7 +51,8 @@
         if expected_preamble:
             expected_preamble = self.parse(expected_preamble)
         if expected_short:
-            expected_short = self.parse(expected_short)
+            # the short preamble doesn't have fail descrs, they are patched in when it is used
+            expected_short = self.parse(expected_short, want_fail_descr=False)
 
         preamble = self.unroll_and_optimize(loop, call_pure_results)
 
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -355,11 +355,21 @@
 
 class BaseTest(object):
 
-    def parse(self, s, boxkinds=None):
+    def parse(self, s, boxkinds=None, want_fail_descr=True):
+        if want_fail_descr:
+            invent_fail_descr = self.invent_fail_descr
+        else:
+            invent_fail_descr = lambda *args: None
         return parse(s, self.cpu, self.namespace,
                      type_system=self.type_system,
                      boxkinds=boxkinds,
-                     invent_fail_descr=self.invent_fail_descr)
+                     invent_fail_descr=invent_fail_descr)
+
+    def add_guard_future_condition(self, res):
+        # invent a GUARD_FUTURE_CONDITION to not have to change all tests
+        if res.operations[-1].getopnum() == rop.JUMP:
+            guard = ResOperation(rop.GUARD_FUTURE_CONDITION, [], None, descr=self.invent_fail_descr(None, -1, []))
+            res.operations.insert(-1, guard)
 
     def invent_fail_descr(self, model, opnum, fail_args):
         if fail_args is None:
@@ -397,6 +407,7 @@
         optimize_trace(metainterp_sd, loop, self.enable_opts)
 
     def unroll_and_optimize(self, loop, call_pure_results=None):
+        self.add_guard_future_condition(loop)
         operations =  loop.operations
         jumpop = operations[-1]
         assert jumpop.getopnum() == rop.JUMP
@@ -408,7 +419,6 @@
 
         preamble = TreeLoop('preamble')
         preamble.inputargs = inputargs
-        preamble.resume_at_jump_descr = FakeDescrWithSnapshot()
 
         token = JitCellToken()
         preamble.operations = [ResOperation(rop.LABEL, inputargs, None, descr=TargetToken(token))] + \
@@ -419,7 +429,6 @@
         assert preamble.operations[-1].getopnum() == rop.LABEL
 
         inliner = Inliner(inputargs, jump_args)
-        loop.resume_at_jump_descr = preamble.resume_at_jump_descr
         loop.operations = [preamble.operations[-1]] + \
                           [inliner.inline_op(op, clone=False) for op in cloned_operations] + \
                           [ResOperation(rop.JUMP, [inliner.inline_arg(a) for a in jump_args],
@@ -450,18 +459,6 @@
     def __eq__(self, other):
         return isinstance(other, FakeDescr)
 
-class FakeDescrWithSnapshot(compile.ResumeGuardDescr):
-    class rd_snapshot:
-        class prev:
-            prev = None
-            boxes = []
-        boxes = []
-    def clone_if_mutable(self):
-        return FakeDescrWithSnapshot()
-    def __eq__(self, other):
-        return isinstance(other, Storage) or isinstance(other, FakeDescrWithSnapshot)
-
-
 def convert_old_style_to_targets(loop, jump):
     newloop = TreeLoop(loop.name)
     newloop.inputargs = loop.inputargs
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py b/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_virtualstate.py
@@ -1,43 +1,103 @@
 from __future__ import with_statement
 import py
-from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.optimizeopt.virtualstate import VirtualStateInfo, VStructStateInfo, \
-     VArrayStateInfo, NotVirtualStateInfo, VirtualState, ShortBoxes
+     VArrayStateInfo, NotVirtualStateInfo, VirtualState, ShortBoxes, GenerateGuardState, \
+     VirtualStatesCantMatch, VArrayStructStateInfo
 from rpython.jit.metainterp.optimizeopt.optimizer import OptValue
 from rpython.jit.metainterp.history import BoxInt, BoxFloat, BoxPtr, ConstInt, ConstPtr
 from rpython.rtyper.lltypesystem import lltype, llmemory
 from rpython.jit.metainterp.optimizeopt.test.test_util import LLtypeMixin, BaseTest, \
-                                                           equaloplists, FakeDescrWithSnapshot
+                                                           equaloplists
 from rpython.jit.metainterp.optimizeopt.intutils import IntBound
+from rpython.jit.metainterp.optimizeopt.virtualize import (VirtualValue,
+        VArrayValue, VStructValue, VArrayStructValue)
 from rpython.jit.metainterp.history import TreeLoop, JitCellToken
 from rpython.jit.metainterp.optimizeopt.test.test_optimizeopt import FakeMetaInterpStaticData
 from rpython.jit.metainterp.resoperation import ResOperation, rop
 
-class TestBasic:
-    someptr1 = LLtypeMixin.myptr
-    someptr2 = LLtypeMixin.myptr2
+class BaseTestGenerateGuards(BaseTest):
+
+    def _box_or_value(self, box_or_value=None):
+        if box_or_value is None:
+            return None, None
+        elif isinstance(box_or_value, OptValue):
+            value = box_or_value
+            box = value.box
+        else:
+            box = box_or_value
+            value = OptValue(box)
+        return value, box
+
+    def guards(self, info1, info2, box_or_value, expected, inputargs=None):
+        value, box = self._box_or_value(box_or_value)
+        if inputargs is None:
+            inputargs = [box]
+        info1.position = info2.position = 0
+        state = GenerateGuardState(self.cpu)
+        info1.generate_guards(info2, value, state)
+        self.compare(state.extra_guards, expected, inputargs)
+
+    def compare(self, guards, expected, inputargs):
+        loop = self.parse(expected)
+        boxmap = {}
+        assert len(loop.inputargs) == len(inputargs)
+        for a, b in zip(loop.inputargs, inputargs):
+            boxmap[a] = b
+        for op in loop.operations:
+            if op.is_guard():
+                op.setdescr(None)
+        assert equaloplists(guards, loop.operations, False,
+                            boxmap)
+
+    def check_no_guards(self, info1, info2, box_or_value=None, state=None):
+        value, _ = self._box_or_value(box_or_value)
+        if info1.position == -1:
+            info1.position = 0
+        if info2.position == -1:
+            info2.position = 0
+        if state is None:
+            state = GenerateGuardState(self.cpu)
+        info1.generate_guards(info2, value, state)
+        assert not state.extra_guards
+        return state
+
+    def check_invalid(self, info1, info2, box_or_value=None, state=None):
+        value, _ = self._box_or_value(box_or_value)
+        if info1.position == -1:
+            info1.position = 0
+        if info2.position == -1:
+            info2.position = 0
+        if state is None:
+            state = GenerateGuardState(self.cpu)
+        with py.test.raises(VirtualStatesCantMatch):
+            info1.generate_guards(info2, value, state)
+
 
     def test_position_generalization(self):
         def postest(info1, info2):
             info1.position = 0
-            assert info1.generalization_of(info1, {}, {})
+            self.check_no_guards(info1, info1)
             info2.position = 0
-            assert info1.generalization_of(info2, {}, {})
+            self.check_no_guards(info1, info2)
             info2.position = 1
-            renum = {}
-            assert info1.generalization_of(info2, renum, {})
-            assert renum == {0:1}
-            assert info1.generalization_of(info2, {0:1}, {})
-            assert info1.generalization_of(info2, {1:1}, {})
-            bad = {}
-            assert not info1.generalization_of(info2, {0:0}, bad)
-            assert info1 in bad and info2 in bad
+            state = self.check_no_guards(info1, info2)
+            assert state.renum == {0:1}
+
+            assert self.check_no_guards(info1, info2, state=state)
+
+            # feed fake renums
+            state.renum = {1: 1}
+            self.check_no_guards(info1, info2, state=state)
+
+            state.renum = {0: 0}
+            self.check_invalid(info1, info2, state=state)
+            assert info1 in state.bad and info2 in state.bad
 
         for BoxType in (BoxInt, BoxFloat, BoxPtr):
             info1 = NotVirtualStateInfo(OptValue(BoxType()))
             info2 = NotVirtualStateInfo(OptValue(BoxType()))
             postest(info1, info2)
-            
+
         info1, info2 = VArrayStateInfo(42), VArrayStateInfo(42)
         info1.fieldstate = info2.fieldstate = []
         postest(info1, info2)
@@ -56,7 +116,7 @@
             info1.position = 0
             info2 = NotVirtualStateInfo(value2)
             info2.position = 0
-            return info1.generalization_of(info2, {}, {})
+            return VirtualState([info1]).generalization_of(VirtualState([info2]), cpu=self.cpu)
 
         assert isgeneral(OptValue(BoxInt()), OptValue(ConstInt(7)))
         assert not isgeneral(OptValue(ConstInt(7)), OptValue(BoxInt()))
@@ -65,10 +125,11 @@
         nonnull = OptValue(BoxPtr())
         nonnull.make_nonnull(0)
         knownclass = OptValue(BoxPtr())
-        knownclass.make_constant_class(ConstPtr(self.someptr1), 0)
+        clsbox = self.cpu.ts.cls_of_box(BoxPtr(self.myptr))
+        knownclass.make_constant_class(clsbox, 0)
         const = OptValue(BoxPtr)
-        const.make_constant_class(ConstPtr(self.someptr1), 0)
-        const.make_constant(ConstPtr(self.someptr1))
+        const.make_constant_class(clsbox, 0)
+        const.make_constant(ConstPtr(self.myptr))
         inorder = [ptr, nonnull, knownclass, const]
         for i in range(len(inorder)):
             for j in range(i, len(inorder)):
@@ -91,48 +152,51 @@
 
         value1 = OptValue(BoxPtr())
         value1.make_nonnull(None)
-        value2 = OptValue(ConstPtr(LLtypeMixin.nullptr))
+        value2 = OptValue(ConstPtr(self.nullptr))
         assert not isgeneral(value1, value2)
 
     def test_field_matching_generalization(self):
         const1 = NotVirtualStateInfo(OptValue(ConstInt(1)))
         const2 = NotVirtualStateInfo(OptValue(ConstInt(2)))
         const1.position = const2.position = 1
-        assert not const1.generalization_of(const2, {}, {})
-        assert not const2.generalization_of(const1, {}, {})
+        self.check_invalid(const1, const2)
+        self.check_invalid(const2, const1)
 
         def fldtst(info1, info2):
             info1.position = info2.position = 0
             info1.fieldstate = [const1]
             info2.fieldstate = [const2]
-            assert not info1.generalization_of(info2, {}, {})
-            assert not info2.generalization_of(info1, {}, {})
-            assert info1.generalization_of(info1, {}, {})
-            assert info2.generalization_of(info2, {}, {})
-        fldtst(VArrayStateInfo(42), VArrayStateInfo(42))
-        fldtst(VStructStateInfo(42, [7]), VStructStateInfo(42, [7]))
-        fldtst(VirtualStateInfo(ConstInt(42), [7]), VirtualStateInfo(ConstInt(42), [7]))
+            self.check_invalid(info1, info2)
+            self.check_invalid(info2, info1)
+            self.check_no_guards(info1, info1)
+            self.check_no_guards(info2, info2)
+        fakedescr = object()
+        fielddescr = object()
+        fldtst(VArrayStateInfo(fakedescr), VArrayStateInfo(fakedescr))
+        fldtst(VStructStateInfo(fakedescr, [fielddescr]), VStructStateInfo(fakedescr, [fielddescr]))
+        fldtst(VirtualStateInfo(ConstInt(42), [fielddescr]), VirtualStateInfo(ConstInt(42), [fielddescr]))
+        fldtst(VArrayStructStateInfo(fakedescr, [[fielddescr]]), VArrayStructStateInfo(fakedescr, [[fielddescr]]))
 
     def test_known_class_generalization(self):
         knownclass1 = OptValue(BoxPtr())
-        knownclass1.make_constant_class(ConstPtr(self.someptr1), 0)
+        knownclass1.make_constant_class(ConstPtr(self.myptr), 0)
         info1 = NotVirtualStateInfo(knownclass1)
         info1.position = 0
         knownclass2 = OptValue(BoxPtr())
-        knownclass2.make_constant_class(ConstPtr(self.someptr1), 0)
+        knownclass2.make_constant_class(ConstPtr(self.myptr), 0)
         info2 = NotVirtualStateInfo(knownclass2)
         info2.position = 0
-        assert info1.generalization_of(info2, {}, {})
-        assert info2.generalization_of(info1, {}, {})
+        self.check_no_guards(info1, info2)
+        self.check_no_guards(info2, info1)
 
         knownclass3 = OptValue(BoxPtr())
-        knownclass3.make_constant_class(ConstPtr(self.someptr2), 0)
+        knownclass3.make_constant_class(ConstPtr(self.myptr2), 0)
         info3 = NotVirtualStateInfo(knownclass3)
         info3.position = 0
-        assert not info1.generalization_of(info3, {}, {})
-        assert not info2.generalization_of(info3, {}, {})
-        assert not info3.generalization_of(info2, {}, {})
-        assert not info3.generalization_of(info1, {}, {})
+        self.check_invalid(info1, info3)
+        self.check_invalid(info2, info3)
+        self.check_invalid(info3, info2)
+        self.check_invalid(info3, info1)
 
 
     def test_circular_generalization(self):
@@ -140,29 +204,157 @@
                      VirtualStateInfo(ConstInt(42), [7])):
             info.position = 0
             info.fieldstate = [info]
-            assert info.generalization_of(info, {}, {})
+            self.check_no_guards(info, info)
 
 
-class BaseTestGenerateGuards(BaseTest):
-    def guards(self, info1, info2, box, expected):
-        info1.position = info2.position = 0
-        guards = []
-        info1.generate_guards(info2, box, self.cpu, guards, {})
-        self.compare(guards, expected, [box])
+    def test_generate_guards_nonvirtual_all_combinations(self):
+        # set up infos
+        unknown_val = OptValue(self.nodebox)
+        unknownnull_val = OptValue(BoxPtr(self.nullptr))
+        unknown_info = NotVirtualStateInfo(unknown_val)
 
-    def compare(self, guards, expected, inputargs):
-        loop = self.parse(expected)
-        boxmap = {}
-        assert len(loop.inputargs) == len(inputargs)
-        for a, b in zip(loop.inputargs, inputargs):
-            boxmap[a] = b
-        for op in loop.operations:
-            if op.is_guard():
-                op.setdescr(None)
-        assert equaloplists(guards, loop.operations, False,
-                            boxmap)        
+        nonnull_val = OptValue(self.nodebox)
+        nonnull_val.make_nonnull(None)
+        nonnull_info = NotVirtualStateInfo(nonnull_val)
+
+        knownclass_val = OptValue(self.nodebox)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox)
+        knownclass_val.make_constant_class(classbox, -1)
+        knownclass_info = NotVirtualStateInfo(knownclass_val)
+        knownclass2_val = OptValue(self.nodebox2)
+        classbox = self.cpu.ts.cls_of_box(self.nodebox2)
+        knownclass2_val.make_constant_class(classbox, -1)
+        knownclass2_info = NotVirtualStateInfo(knownclass2_val)
+
+        constant_val = OptValue(BoxInt())
+        constant_val.make_constant(ConstInt(1))
+        constant_info = NotVirtualStateInfo(constant_val)
+        constclass_val = OptValue(self.nodebox)
+        constclass_val.make_constant(self.nodebox.constbox())
+        constclass_info = NotVirtualStateInfo(constclass_val)
+        constclass2_val = OptValue(self.nodebox)
+        constclass2_val.make_constant(self.nodebox2.constbox())
+        constclass2_info = NotVirtualStateInfo(constclass2_val)
+        constantnull_val = OptValue(ConstPtr(self.nullptr))
+        constantnull_info = NotVirtualStateInfo(constantnull_val)
+
+        # unknown unknown
+        self.check_no_guards(unknown_info, unknown_info, unknown_val)
+        self.check_no_guards(unknown_info, unknown_info)
+
+        # unknown nonnull
+        self.check_no_guards(unknown_info, nonnull_info, nonnull_val)
+        self.check_no_guards(unknown_info, nonnull_info)
+
+        # unknown knownclass
+        self.check_no_guards(unknown_info, knownclass_info, knownclass_val)
+        self.check_no_guards(unknown_info, knownclass_info)
+
+        # unknown constant
+        self.check_no_guards(unknown_info, constant_info, constant_val)
+        self.check_no_guards(unknown_info, constant_info)
+
+
+        # nonnull unknown
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        """
+        self.guards(nonnull_info, unknown_info, unknown_val, expected)
+        self.check_invalid(nonnull_info, unknown_info, unknownnull_val)
+        self.check_invalid(nonnull_info, unknown_info)
+        self.check_invalid(nonnull_info, unknown_info)
+
+        # nonnull nonnull
+        self.check_no_guards(nonnull_info, nonnull_info, nonnull_val)
+        self.check_no_guards(nonnull_info, nonnull_info, nonnull_val)
+
+        # nonnull knownclass
+        self.check_no_guards(nonnull_info, knownclass_info, knownclass_val)
+        self.check_no_guards(nonnull_info, knownclass_info)
+
+        # nonnull constant
+        self.check_no_guards(nonnull_info, constant_info, constant_val)
+        self.check_invalid(nonnull_info, constantnull_info, constantnull_val)
+        self.check_no_guards(nonnull_info, constant_info)
+        self.check_invalid(nonnull_info, constantnull_info)
+
+
+        # knownclass unknown
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(knownclass_info, unknown_info, unknown_val, expected)
+        self.check_invalid(knownclass_info, unknown_info, unknownnull_val)
+        self.check_invalid(knownclass_info, unknown_info, knownclass2_val)
+        self.check_invalid(knownclass_info, unknown_info)
+        self.check_invalid(knownclass_info, unknown_info)
+        self.check_invalid(knownclass_info, unknown_info)
+
+        # knownclass nonnull
+        expected = """
+        [p0]
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(knownclass_info, nonnull_info, knownclass_val, expected)
+        self.check_invalid(knownclass_info, nonnull_info, knownclass2_val)
+        self.check_invalid(knownclass_info, nonnull_info)
+        self.check_invalid(knownclass_info, nonnull_info)
+
+        # knownclass knownclass
+        self.check_no_guards(knownclass_info, knownclass_info, knownclass_val)
+        self.check_invalid(knownclass_info, knownclass2_info, knownclass2_val)
+        self.check_no_guards(knownclass_info, knownclass_info)
+        self.check_invalid(knownclass_info, knownclass2_info)
+
+        # knownclass constant
+        self.check_invalid(knownclass_info, constantnull_info, constantnull_val)
+        self.check_invalid(knownclass_info, constclass2_info, constclass2_val)
+        self.check_invalid(knownclass_info, constantnull_info)
+        self.check_invalid(knownclass_info, constclass2_info)
+
+
+        # constant unknown
+        expected = """
+        [i0]
+        guard_value(i0, 1) []
+        """
+        self.guards(constant_info, unknown_info, constant_val, expected)
+        self.check_invalid(constant_info, unknown_info, unknownnull_val)
+        self.check_invalid(constant_info, unknown_info)
+        self.check_invalid(constant_info, unknown_info)
+
+        # constant nonnull
+        expected = """
+        [i0]
+        guard_value(i0, 1) []
+        """
+        self.guards(constant_info, nonnull_info, constant_val, expected)
+        self.check_invalid(constant_info, nonnull_info, constclass2_val)
+        self.check_invalid(constant_info, nonnull_info)
+        self.check_invalid(constant_info, nonnull_info)
+
+        # constant knownclass
+        expected = """
+        [i0]
+        guard_value(i0, 1) []
+        """
+        self.guards(constant_info, knownclass_info, constant_val, expected)
+        self.check_invalid(constant_info, knownclass_info, unknownnull_val)
+        self.check_invalid(constant_info, knownclass_info)
+        self.check_invalid(constant_info, knownclass_info)
+
+        # constant constant
+        self.check_no_guards(constant_info, constant_info, constant_val)
+        self.check_invalid(constant_info, constantnull_info, constantnull_val)
+        self.check_no_guards(constant_info, constant_info)
+        self.check_invalid(constant_info, constantnull_info)
+
+
     def test_intbounds(self):
-        value1 = OptValue(BoxInt())
+        value1 = OptValue(BoxInt(15))
         value1.intbound.make_ge(IntBound(0, 10))
         value1.intbound.make_le(IntBound(20, 30))
         info1 = NotVirtualStateInfo(value1)
@@ -174,10 +366,19 @@
         i2 = int_le(i0, 30)
         guard_true(i2) []
         """
-        self.guards(info1, info2, BoxInt(15), expected)
-        py.test.raises(InvalidLoop, self.guards,
-                       info1, info2, BoxInt(50), expected)
+        self.guards(info1, info2, value1, expected)
+        self.check_invalid(info1, info2, BoxInt(50))
 
+    def test_intbounds_constant(self):
+        value1 = OptValue(BoxInt(15))
+        value1.intbound.make_ge(IntBound(0, 10))
+        value1.intbound.make_le(IntBound(20, 30))
+        info1 = NotVirtualStateInfo(value1)
+        info2 = NotVirtualStateInfo(OptValue(ConstInt(10000)))
+        self.check_invalid(info1, info2)
+        info1 = NotVirtualStateInfo(value1)
+        info2 = NotVirtualStateInfo(OptValue(ConstInt(11)))
+        self.check_no_guards(info1, info2)
 
     def test_known_class(self):
         value1 = OptValue(self.nodebox)
@@ -191,8 +392,7 @@
         guard_class(p0, ConstClass(node_vtable)) []
         """
         self.guards(info1, info2, self.nodebox, expected)
-        py.test.raises(InvalidLoop, self.guards,
-                       info1, info2, BoxPtr(), expected)
+        self.check_invalid(info1, info2, BoxPtr())
 
     def test_known_class_value(self):
         value1 = OptValue(self.nodebox)
@@ -219,7 +419,7 @@
         self.compare(guards, expected, [box])
 
     def test_equal_inputargs(self):
-        value = OptValue(self.nodebox)        
+        value = OptValue(self.nodebox)
         classbox = self.cpu.ts.cls_of_box(self.nodebox)
         value.make_constant_class(classbox, -1)
         knownclass_info = NotVirtualStateInfo(value)
@@ -242,22 +442,130 @@
 
         expected = """
         [p0]
-        guard_nonnull(p0) []        
+        guard_nonnull(p0) []
         guard_class(p0, ConstClass(node_vtable)) []
         """
-        guards = []
-        vstate1.generate_guards(vstate2, [self.nodebox, self.nodebox], self.cpu, guards)
-        self.compare(guards, expected, [self.nodebox])
+        state = vstate1.generate_guards(vstate2, [value, value], self.cpu)
+        self.compare(state.extra_guards, expected, [self.nodebox])
 
-        with py.test.raises(InvalidLoop):
-            guards = []
-            vstate1.generate_guards(vstate3, [self.nodebox, self.nodebox],
-                                    self.cpu, guards)
-        with py.test.raises(InvalidLoop):
-            guards = []
-            vstate2.generate_guards(vstate3, [self.nodebox, self.nodebox],
-                                    self.cpu, guards)
-        
+        with py.test.raises(VirtualStatesCantMatch):
+            vstate1.generate_guards(vstate3, [value, value],
+                                    self.cpu)
+        with py.test.raises(VirtualStatesCantMatch):
+            vstate2.generate_guards(vstate3, [value, value],
+                                    self.cpu)
+
+
+    def test_generate_guards_on_virtual_fields_matches_array(self):
+        innervalue1 = OptValue(self.nodebox)
+        constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+        innervalue1.make_constant_class(constclassbox, -1)
+        innerinfo1 = NotVirtualStateInfo(innervalue1)
+        innerinfo1.position = 1
+        innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        innerinfo2.position = 1
+
+        descr = object()
+
+        info1 = VArrayStateInfo(descr)
+        info1.fieldstate = [innerinfo1]
+
+        info2 = VArrayStateInfo(descr)
+        info2.fieldstate = [innerinfo2]
+
+        value1 = VArrayValue(descr, None, 1, self.nodebox)
+        value1._items[0] = OptValue(self.nodebox)
+
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(info1, info2, value1, expected, [self.nodebox])
+
+    def test_generate_guards_on_virtual_fields_matches_instance(self):
+        innervalue1 = OptValue(self.nodebox)
+        constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+        innervalue1.make_constant_class(constclassbox, -1)
+        innerinfo1 = NotVirtualStateInfo(innervalue1)
+        innerinfo1.position = 1
+        innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        innerinfo2.position = 1
+
+        info1 = VirtualStateInfo(ConstInt(42), [1])
+        info1.fieldstate = [innerinfo1]
+
+        info2 = VirtualStateInfo(ConstInt(42), [1])
+        info2.fieldstate = [innerinfo2]
+
+        value1 = VirtualValue(self.cpu, constclassbox, self.nodebox)
+        value1._fields = {1: OptValue(self.nodebox)}
+
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(info1, info2, value1, expected, [self.nodebox])
+
+    def test_generate_guards_on_virtual_fields_matches_struct(self):
+        innervalue1 = OptValue(self.nodebox)
+        constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+        innervalue1.make_constant_class(constclassbox, -1)
+        innerinfo1 = NotVirtualStateInfo(innervalue1)
+        innerinfo1.position = 1
+        innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        innerinfo2.position = 1
+
+        structdescr = object()
+
+        info1 = VStructStateInfo(structdescr, [1])
+        info1.fieldstate = [innerinfo1]
+
+        info2 = VStructStateInfo(structdescr, [1])
+        info2.fieldstate = [innerinfo2]
+
+        value1 = VStructValue(self.cpu, structdescr, self.nodebox)
+        value1._fields = {1: OptValue(self.nodebox)}
+
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(info1, info2, value1, expected, [self.nodebox])
+
+    def test_generate_guards_on_virtual_fields_matches_arraystruct(self):
+        innervalue1 = OptValue(self.nodebox)
+        constclassbox = self.cpu.ts.cls_of_box(self.nodebox)
+        innervalue1.make_constant_class(constclassbox, -1)
+        innerinfo1 = NotVirtualStateInfo(innervalue1)
+        innerinfo1.position = 1
+        innerinfo2 = NotVirtualStateInfo(OptValue(self.nodebox))
+        innerinfo2.position = 1
+
+        arraydescr = object()
+        fielddescr = object()
+
+        info1 = VArrayStructStateInfo(arraydescr, [[fielddescr]])
+        info1.fieldstate = [innerinfo1]
+
+        info2 = VArrayStructStateInfo(arraydescr, [[fielddescr]])
+        info2.fieldstate = [innerinfo2]
+
+        value1 = VArrayStructValue(arraydescr, 1, self.nodebox)
+        value1._items[0][fielddescr] = OptValue(self.nodebox)
+
+        expected = """
+        [p0]
+        guard_nonnull(p0) []
+        guard_class(p0, ConstClass(node_vtable)) []
+        """
+        self.guards(info1, info2, value1, expected, [self.nodebox])
+
+    # _________________________________________________________________________
+    # the below tests don't really have anything to do with guard generation
+
     def test_virtuals_with_equal_fields(self):
         info1 = VirtualStateInfo(ConstInt(42), [1, 2])
         value = OptValue(self.nodebox)
@@ -471,7 +779,6 @@
         if hasattr(self, 'callinfocollection'):
             metainterp_sd.callinfocollection = self.callinfocollection
         #
-        bridge.resume_at_jump_descr = FakeDescrWithSnapshot()
         optimize_trace(metainterp_sd, bridge, self.enable_opts)
 
         
@@ -480,6 +787,7 @@
             loops = (loops, )
         loops = [self.parse(loop) for loop in loops]
         bridge = self.parse(bridge)
+        self.add_guard_future_condition(bridge)
         for loop in loops:
             loop.preamble = self.unroll_and_optimize(loop)
         preamble = loops[0].preamble
@@ -615,26 +923,26 @@
 
     def test_constant(self):
         loops = """
-        [p0]
-        p1 = same_as(ConstPtr(myptr))
-        jump(p1)
+        [i0]
+        i1 = same_as(1)
+        jump(i1)
         """, """
-        [p0]
-        p1 = same_as(ConstPtr(myptr2))
-        jump(p1)
+        [i0]
+        i1 = same_as(2)
+        jump(i1)
         """, """
-        [p0]
-        jump(p0)
+        [i0]
+        jump(i0)
         """
         expected = """
-        [p0]
+        [i0]
         jump()
         """
         self.optimize_bridge(loops, loops[0], expected, 'Loop0')
         self.optimize_bridge(loops, loops[1], expected, 'Loop1')
         expected = """
-        [p0]
-        jump(p0)
+        [i0]
+        jump(i0)
         """
         self.optimize_bridge(loops, loops[2], expected, 'Loop2')
 
@@ -658,7 +966,7 @@
         """
         self.optimize_bridge(loop, bridge, expected, p0=self.myptr)
 
-    def test_virtual(self):
+    def test_simple_virtual(self):
         loops = """
         [p0, p1]
         p2 = new_with_vtable(ConstClass(node_vtable))
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -6,7 +6,8 @@
 from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.optimizeopt.generalize import KillHugeIntBounds
 from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer, Optimization
-from rpython.jit.metainterp.optimizeopt.virtualstate import VirtualStateAdder, ShortBoxes, BadVirtualState
+from rpython.jit.metainterp.optimizeopt.virtualstate import (VirtualStateConstructor,
+        ShortBoxes, BadVirtualState, VirtualStatesCantMatch)
 from rpython.jit.metainterp.resoperation import rop, ResOperation
 from rpython.jit.metainterp.resume import Snapshot
 from rpython.rlib.debug import debug_print, debug_start, debug_stop
@@ -53,6 +54,10 @@
         self.optimizer = UnrollableOptimizer(metainterp_sd, loop, optimizations)
         self.boxes_created_this_iteration = None
 
+    def get_virtual_state(self, args):
+        modifier = VirtualStateConstructor(self.optimizer)
+        return modifier.get_virtual_state(args)
+
     def fix_snapshot(self, jump_args, snapshot):
         if snapshot is None:
             return None
@@ -77,6 +82,12 @@
         else:
             start_label = None
 
+        patchguardop = None
+        if len(loop.operations) > 1:
+            patchguardop = loop.operations[-2]
+            if patchguardop.getopnum() != rop.GUARD_FUTURE_CONDITION:
+                patchguardop = None
+
         jumpop = loop.operations[-1]
         if jumpop.getopnum() == rop.JUMP or jumpop.getopnum() == rop.LABEL:
             loop.operations = loop.operations[:-1]
@@ -94,7 +105,7 @@
         stop_label = ResOperation(rop.LABEL, jumpop.getarglist(), None, TargetToken(cell_token))
 
         if jumpop.getopnum() == rop.JUMP:
-            if self.jump_to_already_compiled_trace(jumpop):
+            if self.jump_to_already_compiled_trace(jumpop, patchguardop):
                 # Found a compiled trace to jump to
                 if self.short:
                     # Construct our short preamble
@@ -108,7 +119,7 @@
                                       descr=start_label.getdescr())
                 if self.short:
                     # Construct our short preamble
-                    self.close_loop(start_label, jumpop)
+                    self.close_loop(start_label, jumpop, patchguardop)
                 else:
                     self.optimizer.send_extra_operation(jumpop)
                 return
@@ -147,28 +158,14 @@
         start_target = start_label.getdescr()
         assert isinstance(stop_target, TargetToken)
         assert isinstance(start_target, TargetToken)
-        if stop_target.targeting_jitcell_token is not start_target.targeting_jitcell_token:
-            return False
+        return stop_target.targeting_jitcell_token is start_target.targeting_jitcell_token
 
-        return True
-
-        #args = stop_label.getarglist()
-        #modifier = VirtualStateAdder(self.optimizer)
-        #virtual_state = modifier.get_virtual_state(args)
-        #if self.initial_virtual_state.generalization_of(virtual_state):
-        #    return True
 
     def export_state(self, targetop):
         original_jump_args = targetop.getarglist()
         jump_args = [self.getvalue(a).get_key_box() for a in original_jump_args]
 
-        assert self.optimizer.loop.resume_at_jump_descr
-        resume_at_jump_descr = self.optimizer.loop.resume_at_jump_descr.clone_if_mutable()
-        assert isinstance(resume_at_jump_descr, ResumeGuardDescr)
-        resume_at_jump_descr.rd_snapshot = self.fix_snapshot(jump_args, resume_at_jump_descr.rd_snapshot)
-
-        modifier = VirtualStateAdder(self.optimizer)
-        virtual_state = modifier.get_virtual_state(jump_args)
+        virtual_state = self.get_virtual_state(jump_args)
 
         values = [self.getvalue(arg) for arg in jump_args]
         inputargs = virtual_state.make_inputargs(values, self.optimizer)
@@ -195,7 +192,6 @@
         targetop.initarglist(inputargs)
         target_token.virtual_state = virtual_state
         target_token.short_preamble = [ResOperation(rop.LABEL, short_inputargs, None)]
-        target_token.resume_at_jump_descr = resume_at_jump_descr
 
         exported_values = {}
         for box in inputargs:
@@ -222,15 +218,13 @@
         if not exported_state:
             # No state exported, construct one without virtuals
             self.short = None
-            modifier = VirtualStateAdder(self.optimizer)
-            virtual_state = modifier.get_virtual_state(self.inputargs)
+            virtual_state = self.get_virtual_state(self.inputargs)
             self.initial_virtual_state = virtual_state
             return
 
         self.short = target_token.short_preamble[:]
         self.short_seen = {}
         self.short_boxes = exported_state.short_boxes
-        self.short_resume_at_jump_descr = target_token.resume_at_jump_descr
         self.initial_virtual_state = target_token.virtual_state
 
         seen = {}
@@ -286,19 +280,13 @@
         self.boxes_created_this_iteration = {}
         i = 0
         while i < len(newoperations):
-            op = newoperations[i]
-            self.boxes_created_this_iteration[op.result] = None
-            args = op.getarglist()
-            if op.is_guard():
-                args = args + op.getfailargs()
-            for a in args:
-                self.import_box(a, inputargs, short_jumpargs, [])
+            self._import_op(newoperations[i], inputargs, short_jumpargs, [])
             i += 1
             newoperations = self.optimizer.get_newoperations()
         self.short.append(ResOperation(rop.JUMP, short_jumpargs, None, descr=start_label.getdescr()))
         self.finalize_short_preamble(start_label)
 
-    def close_loop(self, start_label, jumpop):
+    def close_loop(self, start_label, jumpop, patchguardop):
         virtual_state = self.initial_virtual_state
         short_inputargs = self.short[0].getarglist()
         inputargs = self.inputargs
@@ -330,19 +318,8 @@
 
             args[short_inputargs[i]] = jmp_to_short_args[i]
         self.short_inliner = Inliner(short_inputargs, jmp_to_short_args)
-        i = 1
-        while i < len(self.short):
-            # Note that self.short might be extended during this loop
-            op = self.short[i]
-            newop = self.short_inliner.inline_op(op)
-            self.optimizer.send_extra_operation(newop)
-            if op.result in self.short_boxes.assumed_classes:
-                classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
-                assumed_classbox = self.short_boxes.assumed_classes[op.result]
-                if not classbox or not classbox.same_constant(assumed_classbox):
-                    raise InvalidLoop('Class of opaque pointer needed in short ' +
-                                      'preamble unknown at end of loop')
-            i += 1
+        self._inline_short_preamble(self.short, self.short_inliner,
+                                    patchguardop, self.short_boxes.assumed_classes)
 
         # Import boxes produced in the preamble but used in the loop
         newoperations = self.optimizer.get_newoperations()
@@ -357,19 +334,7 @@
                     self.import_box(a, inputargs, short_jumpargs, jumpargs)
                     j += 1
             else:
-                op = newoperations[i]
-
-                self.boxes_created_this_iteration[op.result] = None
-                args = op.getarglist()
-                if op.is_guard():
-                    args = args + op.getfailargs()
-
-                #if self.optimizer.loop.logops:
-                #    debug_print('OP: ' + self.optimizer.loop.logops.repr_of_resop(op))
-                for a in args:
-                    #if self.optimizer.loop.logops:
-                    #    debug_print('A:  ' + self.optimizer.loop.logops.repr_of_arg(a))
-                    self.import_box(a, inputargs, short_jumpargs, jumpargs)
+                self._import_op(newoperations[i], inputargs, short_jumpargs, jumpargs)
                 i += 1
             newoperations = self.optimizer.get_newoperations()
 
@@ -379,12 +344,12 @@
 
         # Verify that the virtual state at the end of the loop is one
         # that is compatible with the virtual state at the start of the loop
-        modifier = VirtualStateAdder(self.optimizer)
-        final_virtual_state = modifier.get_virtual_state(original_jumpargs)
+        final_virtual_state = self.get_virtual_state(original_jumpargs)
         #debug_start('jit-log-virtualstate')
         #virtual_state.debug_print('Closed loop with ')
         bad = {}
-        if not virtual_state.generalization_of(final_virtual_state, bad):
+        if not virtual_state.generalization_of(final_virtual_state, bad,
+                                               cpu=self.optimizer.cpu):
             # We ended up with a virtual state that is not compatible
             # and we are thus unable to jump to the start of the loop
             #final_virtual_state.debug_print("Bad virtual state at end of loop, ",
@@ -417,8 +382,7 @@
             if op.is_guard():
                 op = op.clone()
                 op.setfailargs(None)
-                descr = target_token.resume_at_jump_descr.clone_if_mutable()
-                op.setdescr(descr)
+                op.setdescr(None) # will be set to a proper descr when the preamble is used
                 short[i] = op
 
         # Clone ops and boxes to get private versions and
@@ -440,8 +404,6 @@
             if op.result and op.result in self.short_boxes.assumed_classes:
                 target_token.assumed_classes[newop.result] = self.short_boxes.assumed_classes[op.result]
             short[i] = newop
-        target_token.resume_at_jump_descr = target_token.resume_at_jump_descr.clone_if_mutable()
-        inliner.inline_descr_inplace(target_token.resume_at_jump_descr)
 
         # Forget the values to allow them to be freed
         for box in short[0].getarglist():
@@ -485,8 +447,7 @@
             if not isinstance(a, Const) and a not in self.short_seen:
                 self.add_op_to_short(self.short_boxes.producer(a), emit, guards_needed)
         if op.is_guard():
-            descr = self.short_resume_at_jump_descr.clone_if_mutable()
-            op.setdescr(descr)
+            op.setdescr(None) # will be set to a proper descr when the preamble is used
 
         if guards_needed and self.short_boxes.has_producer(op.result):
             value_guards = self.getvalue(op.result).make_guards(op.result)
@@ -528,7 +489,17 @@
             box = self.optimizer.values[box].force_box(self.optimizer)
         jumpargs.append(box)
 
-    def jump_to_already_compiled_trace(self, jumpop):
+
+    def _import_op(self, op, inputargs, short_jumpargs, jumpargs):
+        self.boxes_created_this_iteration[op.result] = None
+        args = op.getarglist()
+        if op.is_guard():
+            args = args + op.getfailargs()
+
+        for a in args:
+            self.import_box(a, inputargs, short_jumpargs, jumpargs)
+
+    def jump_to_already_compiled_trace(self, jumpop, patchguardop):
         assert jumpop.getopnum() == rop.JUMP
         cell_token = jumpop.getdescr()
 
@@ -543,72 +514,84 @@
             return True
 
         args = jumpop.getarglist()
-        modifier = VirtualStateAdder(self.optimizer)
-        virtual_state = modifier.get_virtual_state(args)
+        virtual_state = self.get_virtual_state(args)
+        values = [self.getvalue(arg)
+                  for arg in jumpop.getarglist()]
         debug_start('jit-log-virtualstate')
-        virtual_state.debug_print("Looking for ")
+        virtual_state.debug_print("Looking for ", metainterp_sd=self.optimizer.metainterp_sd)
 
         for target in cell_token.target_tokens:
             if not target.virtual_state:
                 continue
-            ok = False
             extra_guards = []
 
-            bad = {}
-            debugmsg = 'Did not match '
-            if target.virtual_state.generalization_of(virtual_state, bad):
-                ok = True
-                debugmsg = 'Matched '
-            else:
-                try:
-                    cpu = self.optimizer.cpu
-                    target.virtual_state.generate_guards(virtual_state,
-                                                         args, cpu,
-                                                         extra_guards)
+            try:
+                cpu = self.optimizer.cpu
+                state = target.virtual_state.generate_guards(virtual_state,
+                                                             values,
+                                                             cpu)
 
-                    ok = True
+                extra_guards = state.extra_guards
+                if extra_guards:
                     debugmsg = 'Guarded to match '
-                except InvalidLoop:
-                    pass
-            target.virtual_state.debug_print(debugmsg, bad)
+                else:
+                    debugmsg = 'Matched '
+            except VirtualStatesCantMatch, e:
+                debugmsg = 'Did not match:\n%s\n' % (e.msg, )
+                target.virtual_state.debug_print(debugmsg, e.state.bad, metainterp_sd=self.optimizer.metainterp_sd)
+                continue
 
-            if ok:
-                debug_stop('jit-log-virtualstate')
+            assert patchguardop is not None or (extra_guards == [] and len(target.short_preamble) == 1)
 
-                values = [self.getvalue(arg)
-                          for arg in jumpop.getarglist()]
-                args = target.virtual_state.make_inputargs(values, self.optimizer,
-                                                           keyboxes=True)
-                short_inputargs = target.short_preamble[0].getarglist()
-                inliner = Inliner(short_inputargs, args)
+            target.virtual_state.debug_print(debugmsg, {})
 
-                for guard in extra_guards:
-                    if guard.is_guard():
-                        descr = target.resume_at_jump_descr.clone_if_mutable()
-                        inliner.inline_descr_inplace(descr)
-                        guard.setdescr(descr)
-                    self.optimizer.send_extra_operation(guard)
+            debug_stop('jit-log-virtualstate')
 
-                try:
-                    for shop in target.short_preamble[1:]:
-                        newop = inliner.inline_op(shop)
-                        self.optimizer.send_extra_operation(newop)
-                        if shop.result in target.assumed_classes:
-                            classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
-                            if not classbox or not classbox.same_constant(target.assumed_classes[shop.result]):
-                                raise InvalidLoop('The class of an opaque pointer at the end ' +
-                                                  'of the bridge does not mach the class ' +
-                                                  'it has at the start of the target loop')
-                except InvalidLoop:
-                    #debug_print("Inlining failed unexpectedly",
-                    #            "jumping to preamble instead")
-                    assert cell_token.target_tokens[0].virtual_state is None
-                    jumpop.setdescr(cell_token.target_tokens[0])
-                    self.optimizer.send_extra_operation(jumpop)
-                return True
+            args = target.virtual_state.make_inputargs(values, self.optimizer,
+                                                       keyboxes=True)
+            short_inputargs = target.short_preamble[0].getarglist()
+            inliner = Inliner(short_inputargs, args)
+
+            for guard in extra_guards:
+                if guard.is_guard():
+                    descr = patchguardop.getdescr().clone_if_mutable()
+                    guard.setdescr(descr)
+                self.optimizer.send_extra_operation(guard)
+
+            try:
+                # NB: the short_preamble ends with a jump
+                self._inline_short_preamble(target.short_preamble, inliner, patchguardop, target.assumed_classes)
+            except InvalidLoop:
+                #debug_print("Inlining failed unexpectedly",
+                #            "jumping to preamble instead")
+                assert cell_token.target_tokens[0].virtual_state is None
+                jumpop.setdescr(cell_token.target_tokens[0])
+                self.optimizer.send_extra_operation(jumpop)
+            return True
         debug_stop('jit-log-virtualstate')
         return False
 
+    def _inline_short_preamble(self, short_preamble, inliner, patchguardop, assumed_classes):
+        i = 1
+        # XXX this is intentiontal :-(. short_preamble can change during the
+        # loop in some cases
+        while i < len(short_preamble):
+            shop = short_preamble[i]
+            newop = inliner.inline_op(shop)
+            if newop.is_guard():
+                if not patchguardop:
+                    raise InvalidLoop("would like to have short preamble, but it has a guard and there's no guard_future_condition")
+                descr = patchguardop.getdescr().clone_if_mutable()
+                newop.setdescr(descr)
+            self.optimizer.send_extra_operation(newop)
+            if shop.result in assumed_classes:
+                classbox = self.getvalue(newop.result).get_constant_class(self.optimizer.cpu)
+                if not classbox or not classbox.same_constant(assumed_classes[shop.result]):
+                    raise InvalidLoop('The class of an opaque pointer before the jump ' +
+                                      'does not mach the class ' +
+                                      'it has at the start of the target loop')
+            i += 1
+
 
 class ValueImporter(object):
     def __init__(self, unroll, value, op):
diff --git a/rpython/jit/metainterp/optimizeopt/virtualize.py b/rpython/jit/metainterp/optimizeopt/virtualize.py
--- a/rpython/jit/metainterp/optimizeopt/virtualize.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualize.py
@@ -10,7 +10,7 @@
 
 from rpython.jit.metainterp.optimizeopt.rawbuffer import RawBuffer, InvalidRawOperation
 from rpython.jit.metainterp.resoperation import rop, ResOperation
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.objectmodel import we_are_translated, specialize
 
 
 class AbstractVirtualValue(optimizer.OptValue):
@@ -45,27 +45,17 @@
             return value
         return OptValue(self.force_box(optforce))
 
-    def get_args_for_fail(self, modifier):
+    def visitor_walk_recursive(self, visitor):
         # checks for recursion: it is False unless
         # we have already seen the very same keybox
-        if self.box is None and not modifier.already_seen_virtual(self.keybox):
-            self._get_args_for_fail(modifier)
+        if self.box is None and not visitor.already_seen_virtual(self.keybox):
+            self._visitor_walk_recursive(visitor)
 
-    def _get_args_for_fail(self, modifier):
+    def _visitor_walk_recursive(self, visitor):
         raise NotImplementedError("abstract base")
 
-    def make_virtual_info(self, modifier, fieldnums):
-        if fieldnums is None:
-            return self._make_virtual(modifier)
-        vinfo = self._cached_vinfo
-        if vinfo is not None and vinfo.equals(fieldnums):
-            return vinfo
-        vinfo = self._make_virtual(modifier)
-        vinfo.set_content(fieldnums)
-        self._cached_vinfo = vinfo
-        return vinfo
-
-    def _make_virtual(self, modifier):
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
         raise NotImplementedError("abstract base")
 
     def _really_force(self, optforce):
@@ -202,13 +192,13 @@
             self._cached_sorted_fields = lst
         return lst
 
-    def _get_args_for_fail(self, modifier):
+    def _visitor_walk_recursive(self, visitor):
         lst = self._get_field_descr_list()
         fieldboxes = [self._fields[ofs].get_key_box() for ofs in lst]
-        modifier.register_virtual_fields(self.keybox, fieldboxes)
+        visitor.register_virtual_fields(self.keybox, fieldboxes)
         for ofs in lst:
             fieldvalue = self._fields[ofs]
-            fieldvalue.get_args_for_fail(modifier)
+            fieldvalue.visitor_walk_recursive(visitor)
 
 class VirtualValue(AbstractVirtualStructValue):
     level = optimizer.LEVEL_KNOWNCLASS
@@ -218,9 +208,10 @@
         assert isinstance(known_class, Const)
         self.known_class = known_class
 
-    def _make_virtual(self, modifier):
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
         fielddescrs = self._get_field_descr_list()
-        return modifier.make_virtual(self.known_class, fielddescrs)
+        return visitor.visit_virtual(self.known_class, fielddescrs)
 
     def _get_descr(self):
         return vtable2descr(self.cpu, self.known_class.getint())
@@ -238,9 +229,10 @@
         AbstractVirtualStructValue.__init__(self, cpu, keybox, source_op)
         self.structdescr = structdescr
 
-    def _make_virtual(self, modifier):
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
         fielddescrs = self._get_field_descr_list()
-        return modifier.make_vstruct(self.structdescr, fielddescrs)
+        return visitor.visit_vstruct(self.structdescr, fielddescrs)
 
     def _get_descr(self):
         return self.structdescr
@@ -260,15 +252,15 @@
     def set_item_value(self, i, newval):
         raise NotImplementedError
 
-    def _get_args_for_fail(self, modifier):
+    def _visitor_walk_recursive(self, visitor):
         itemboxes = []
         for i in range(self.getlength()):
             itemvalue = self.get_item_value(i)
             itemboxes.append(itemvalue.get_key_box())
-        modifier.register_virtual_fields(self.keybox, itemboxes)
+        visitor.register_virtual_fields(self.keybox, itemboxes)
         for i in range(self.getlength()):
             itemvalue = self.get_item_value(i)
-            itemvalue.get_args_for_fail(modifier)
+            itemvalue.visitor_walk_recursive(visitor)
 
 
 class VArrayValue(AbstractVArrayValue):
@@ -326,8 +318,9 @@
                                   descr=self.arraydescr)
                 optforce.emit_operation(op)
 
-    def _make_virtual(self, modifier):
-        return modifier.make_varray(self.arraydescr)
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
+        return visitor.visit_varray(self.arraydescr)
 
 
 class VArrayStructValue(AbstractVirtualValue):
@@ -373,16 +366,16 @@
             descrs.append(item_descrs)
         return descrs
 
-    def _get_args_for_fail(self, modifier):
+    def _visitor_walk_recursive(self, visitor):
         itemdescrs = self._get_list_of_descrs()
         itemboxes = []
         for i in range(len(self._items)):
             for descr in itemdescrs[i]:
                 itemboxes.append(self._items[i][descr].get_key_box())
-        modifier.register_virtual_fields(self.keybox, itemboxes)
+        visitor.register_virtual_fields(self.keybox, itemboxes)
         for i in range(len(self._items)):
             for descr in itemdescrs[i]:
-                self._items[i][descr].get_args_for_fail(modifier)
+                self._items[i][descr].visitor_walk_recursive(visitor)
 
     def force_at_end_of_preamble(self, already_forced, optforce):
         if self in already_forced:
@@ -393,8 +386,9 @@
                 self._items[index][descr] = self._items[index][descr].force_at_end_of_preamble(already_forced, optforce)
         return self
 
-    def _make_virtual(self, modifier):
-        return modifier.make_varraystruct(self.arraydescr, self._get_list_of_descrs())
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
+        return visitor.visit_varraystruct(self.arraydescr, self._get_list_of_descrs())
 
 
 class VRawBufferValue(AbstractVArrayValue):
@@ -442,11 +436,12 @@
                               descr=descr)
             optforce.emit_operation(op)
 
-    def _make_virtual(self, modifier):
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
         # I *think* we need to make a copy of offsets and descrs because we
         # want a snapshot of the virtual state right now: if we grow more
         # elements later, we don't want them to go in this virtual state
-        return modifier.make_vrawbuffer(self.size,
+        return visitor.visit_vrawbuffer(self.size,
                                         self.buffer.offsets[:],
                                         self.buffer.descrs[:])
 
@@ -474,13 +469,14 @@
     def getitem_raw(self, offset, length, descr):
         return self.rawbuffer_value.getitem_raw(self.offset+offset, length, descr)
 
-    def _get_args_for_fail(self, modifier):
+    def _visitor_walk_recursive(self, visitor):
         box = self.rawbuffer_value.get_key_box()
-        modifier.register_virtual_fields(self.keybox, [box])
-        self.rawbuffer_value.get_args_for_fail(modifier)
+        visitor.register_virtual_fields(self.keybox, [box])
+        self.rawbuffer_value.visitor_walk_recursive(visitor)
 
-    def _make_virtual(self, modifier):
-        return modifier.make_vrawslice(self.offset)
+    @specialize.argtype(1)
+    def _visitor_dispatch_virtual_type(self, visitor):
+        return visitor.visit_vrawslice(self.offset)
 
 
 class OptVirtualize(optimizer.Optimization):
diff --git a/rpython/jit/metainterp/optimizeopt/virtualstate.py b/rpython/jit/metainterp/optimizeopt/virtualstate.py
--- a/rpython/jit/metainterp/optimizeopt/virtualstate.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualstate.py
@@ -1,46 +1,74 @@
-from rpython.jit.metainterp import resume
-from rpython.jit.metainterp.history import BoxInt, ConstInt, BoxPtr, Const
-from rpython.jit.metainterp.optimize import InvalidLoop
+from rpython.jit.metainterp.walkvirtual import VirtualVisitor
+from rpython.jit.metainterp.history import (BoxInt, ConstInt, BoxPtr, Const,
+        ConstPtr, ConstFloat)
 from rpython.jit.metainterp.optimizeopt import virtualize
 from rpython.jit.metainterp.optimizeopt.intutils import IntUnbounded
 from rpython.jit.metainterp.optimizeopt.optimizer import (LEVEL_CONSTANT,
-    LEVEL_KNOWNCLASS, LEVEL_NONNULL, LEVEL_UNKNOWN)
+    LEVEL_KNOWNCLASS, LEVEL_NONNULL, LEVEL_UNKNOWN, OptValue)
 from rpython.jit.metainterp.resoperation import rop, ResOperation
 from rpython.rlib.debug import debug_start, debug_stop, debug_print
-from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.objectmodel import we_are_translated, import_from_mixin
 
 
 class BadVirtualState(Exception):
     pass
 
+class VirtualStatesCantMatch(Exception):
+    def __init__(self, msg='?', state=None):
+        self.msg = msg
+        self.state = state
 
-class AbstractVirtualStateInfo(resume.AbstractVirtualInfo):
+class GenerateGuardState(object):
+    def __init__(self, cpu=None, guards=None, renum=None, bad=None):
+        self.cpu = cpu
+        if guards is None:
+            guards = []
+        self.extra_guards = guards
+        if renum is None:
+            renum = {}
+        self.renum = renum
+        if bad is None:
+            bad = {}
+        self.bad = bad
+
+class AbstractVirtualStateInfo(object):
     position = -1
 
-    def generalization_of(self, other, renum, bad):
+    def generate_guards(self, other, value, state):
+        """ generate guards (output in the list extra_guards) that make runtime
+        values of the shape other match the shape of self. if that's not
+        possible, VirtualStatesCantMatch is thrown and bad gets keys set which
+        parts of the state are the problem.
+
+        the function can peek into value (and particularly also the boxes in
+        the value) as a guiding heuristic whether making such guards makes
+        sense. if None is passed in for value, no guard is ever generated, and
+        this function degenerates to a generalization check."""
+        assert value is None or isinstance(value, OptValue)
         assert self.position != -1
-        if self.position in renum:
-            result = renum[self.position] == other.position
+        if self.position in state.renum:
+            if state.renum[self.position] != other.position:
+                state.bad[self] = state.bad[other] = None
+                raise VirtualStatesCantMatch(
+                        'The numbering of the virtual states does not ' +
+                        'match. This means that two virtual fields ' +
+                        'have been set to the same Box in one of the ' +
+                        'virtual states but not in the other.',
+                        state)
         else:
-            renum[self.position] = other.position
-            result = self.generalization_of_renumbering_done(other, renum, bad)
-        if not result:
-            bad[self] = bad[other] = None
-        return result
+            state.renum[self.position] = other.position
+            try:
+                self._generate_guards(other, value, state)
+            except VirtualStatesCantMatch, e:
+                state.bad[self] = state.bad[other] = None
+                if e.state is None:
+                    e.state = state
+                raise e
 
-    def generate_guards(self, other, box, cpu, extra_guards, renum):
-        if self.generalization_of(other, renum, {}):
-            return
-        if renum[self.position] != other.position:
-            raise InvalidLoop('The numbering of the virtual states does not ' +
-                              'match. This means that two virtual fields ' +
-                              'have been set to the same Box in one of the ' +
-                              'virtual states but not in the other.')
-        self._generate_guards(other, box, cpu, extra_guards)
-
-    def _generate_guards(self, other, box, cpu, extra_guards):
-        raise InvalidLoop('Generating guards for making the VirtualStates ' +
-                          'at hand match have not been implemented')
+    def _generate_guards(self, other, value, state):
+        raise VirtualStatesCantMatch(
+                'Generating guards for making the VirtualStates ' +
+                'at hand match have not been implemented')
 
     def enum_forced_boxes(self, boxes, value, optimizer):
         raise NotImplementedError
@@ -55,7 +83,7 @@
     def _enum(self, virtual_state):
         raise NotImplementedError
 
-    def debug_print(self, indent, seen, bad):
+    def debug_print(self, indent, seen, bad, metainterp_sd):
         mark = ''
         if self in bad:
             mark = '*'
@@ -63,7 +91,7 @@
         if self not in seen:
             seen[self] = True
             for s in self.fieldstate:
-                s.debug_print(indent + "    ", seen, bad)
+                s.debug_print(indent + "    ", seen, bad, metainterp_sd)
         else:
             debug_print(indent + "    ...")
 
@@ -75,26 +103,31 @@
     def __init__(self, fielddescrs):
         self.fielddescrs = fielddescrs
 
-    def generalization_of_renumbering_done(self, other, renum, bad):
-        if not self._generalization_of(other):
-            return False
+    def _generate_guards(self, other, value, state):
+        if not self._generalization_of_structpart(other):
+            raise VirtualStatesCantMatch("different kinds of structs")
 
         assert isinstance(other, AbstractVirtualStructStateInfo)
         assert len(self.fielddescrs) == len(self.fieldstate)
         assert len(other.fielddescrs) == len(other.fieldstate)
+        if value is not None:
+            assert isinstance(value, virtualize.AbstractVirtualStructValue)
+            assert value.is_virtual()
+
         if len(self.fielddescrs) != len(other.fielddescrs):
-            return False
+            raise VirtualStatesCantMatch("field descrs don't match")
 
         for i in range(len(self.fielddescrs)):
             if other.fielddescrs[i] is not self.fielddescrs[i]:
-                return False
-            if not self.fieldstate[i].generalization_of(other.fieldstate[i],
-                                                        renum, bad):
-                return False
+                raise VirtualStatesCantMatch("field descrs don't match")
+            if value is not None:
+                v = value._fields[self.fielddescrs[i]] # must be there
+            else:
+                v = None
+            self.fieldstate[i].generate_guards(other.fieldstate[i], v, state)
 
-        return True
 
-    def _generalization_of(self, other):
+    def _generalization_of_structpart(self, other):
         raise NotImplementedError
 
     def enum_forced_boxes(self, boxes, value, optimizer):
@@ -121,10 +154,11 @@
         AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
         self.known_class = known_class
 
-    def _generalization_of(self, other):
+    def _generalization_of_structpart(self, other):
         return (isinstance(other, VirtualStateInfo) and
                 self.known_class.same_constant(other.known_class))
 
+
     def debug_header(self, indent):
         debug_print(indent + 'VirtualStateInfo(%d):' % self.position)
 
@@ -134,7 +168,7 @@
         AbstractVirtualStructStateInfo.__init__(self, fielddescrs)
         self.typedescr = typedescr
 
-    def _generalization_of(self, other):
+    def _generalization_of_structpart(self, other):
         return (isinstance(other, VStructStateInfo) and
                 self.typedescr is other.typedescr)
 
@@ -147,20 +181,20 @@
     def __init__(self, arraydescr):
         self.arraydescr = arraydescr
 
-    def _generalization_of(self, other):
-        return (isinstance(other, VArrayStateInfo) and
-            self.arraydescr is other.arraydescr)
-
-    def generalization_of_renumbering_done(self, other, renum, bad):
-        if not self._generalization_of(other):
-            return False
+    def _generate_guards(self, other, value, state):
+        if not isinstance(other, VArrayStateInfo):
+            raise VirtualStatesCantMatch("other is not an array")
+        if self.arraydescr is not other.arraydescr:
+            raise VirtualStatesCantMatch("other is a different kind of array")
         if len(self.fieldstate) != len(other.fieldstate):
-            return False
+            raise VirtualStatesCantMatch("other has a different length")
+        v = None
         for i in range(len(self.fieldstate)):
-            if not self.fieldstate[i].generalization_of(other.fieldstate[i],
-                                                        renum, bad):
-                return False
-        return True
+            if value is not None:
+                assert isinstance(value, virtualize.VArrayValue)
+                v = value._items[i]
+            self.fieldstate[i].generate_guards(other.fieldstate[i],
+                                               v, state)
 
     def enum_forced_boxes(self, boxes, value, optimizer):
         if not isinstance(value, virtualize.VArrayValue):
@@ -188,30 +222,31 @@
         self.arraydescr = arraydescr
         self.fielddescrs = fielddescrs
 
-    def generalization_of_renumbering_done(self, other, renum, bad):
-        if not self._generalization_of(other):
-            return False
+    def _generate_guards(self, other, value, state):
+        if not isinstance(other, VArrayStructStateInfo):
+            raise VirtualStatesCantMatch("other is not an VArrayStructStateInfo")
+        if self.arraydescr is not other.arraydescr:
+            raise VirtualStatesCantMatch("other is a different kind of array")
 
-        assert isinstance(other, VArrayStructStateInfo)
         if len(self.fielddescrs) != len(other.fielddescrs):
-            return False
+            raise VirtualStatesCantMatch("other has a different length")
 
         p = 0
+        v = None
         for i in range(len(self.fielddescrs)):
             if len(self.fielddescrs[i]) != len(other.fielddescrs[i]):
-                return False
+                raise VirtualStatesCantMatch("other has a different length")
             for j in range(len(self.fielddescrs[i])):
-                if self.fielddescrs[i][j] is not other.fielddescrs[i][j]:
-                    return False
-                if not self.fieldstate[p].generalization_of(other.fieldstate[p],
-                                                            renum, bad):
-                    return False
+                descr = self.fielddescrs[i][j]
+                if descr is not other.fielddescrs[i][j]:
+                    raise VirtualStatesCantMatch("other is a different kind of array")
+                if value is not None:
+                    assert isinstance(value, virtualize.VArrayStructValue)
+                    v = value._items[i][descr]
+                self.fieldstate[p].generate_guards(other.fieldstate[p],
+                                                   v,
+                                                   state)
                 p += 1
-        return True
-
-    def _generalization_of(self, other):
-        return (isinstance(other, VArrayStructStateInfo) and
-            self.arraydescr is other.arraydescr)
 
     def _enum(self, virtual_state):
         for s in self.fieldstate:
@@ -256,97 +291,109 @@
         self.position_in_notvirtuals = -1
         self.lenbound = value.lenbound
 
-    def generalization_of_renumbering_done(self, other, renum, bad):
+
+    def _generate_guards(self, other, value, state):
+        if value is None or self.is_opaque:
+            box = None # generating guards for opaque pointers isn't safe
+        else:
+            box = value.box
         # XXX This will always retrace instead of forcing anything which
         # might be what we want sometimes?
         if not isinstance(other, NotVirtualStateInfo):
-            return False
-        if other.level < self.level:
-            return False
-        if self.level == LEVEL_CONSTANT:
-            if not self.constbox.same_constant(other.constbox):
-                return False
+            raise VirtualStatesCantMatch(
+                    'The VirtualStates does not match as a ' +
+                    'virtual appears where a pointer is needed ' +
+                    'and it is too late to force it.')
+
+
+        extra_guards = state.extra_guards
+        cpu = state.cpu
+        if self.lenbound and not self.lenbound.generalization_of(other.lenbound):
+            raise VirtualStatesCantMatch("length bound does not match")
+
+        if self.level == LEVEL_UNKNOWN:
+            # confusingly enough, this is done also for pointers
+            # which have the full range as the "bound", so it always works
+            return self._generate_guards_intbounds(other, box, extra_guards)
+
+        # the following conditions often peek into the runtime value that the
+        # box had when tracing. This value is only used as an educated guess.
+        # It is used here to choose between either emitting a guard and jumping
+        # to an existing compiled loop or retracing the loop. Both alternatives
+        # will always generate correct behaviour, but performance will differ.
+        elif self.level == LEVEL_NONNULL:
+            if other.level == LEVEL_UNKNOWN:
+                if box is not None and box.nonnull():
+                    op = ResOperation(rop.GUARD_NONNULL, [box], None)
+                    extra_guards.append(op)
+                    return
+                else:
+                    raise VirtualStatesCantMatch("other not known to be nonnull")
+            elif other.level == LEVEL_NONNULL:
+                return
+            elif other.level == LEVEL_KNOWNCLASS:
+                return # implies nonnull
+            else:
+                assert other.level == LEVEL_CONSTANT
+                assert other.constbox
+                if not other.constbox.nonnull():
+                    raise VirtualStatesCantMatch("constant is null")
+                return
+
         elif self.level == LEVEL_KNOWNCLASS:
-            if not self.known_class.same_constant(other.known_class):
-                return False
-        elif self.level == LEVEL_NONNULL:
-            if other.constbox and not other.constbox.nonnull():
-                return False
+            if other.level == LEVEL_UNKNOWN:
+                if (box and box.nonnull() and
+                        self.known_class.same_constant(cpu.ts.cls_of_box(box))):
+                    op = ResOperation(rop.GUARD_NONNULL, [box], None)
+                    extra_guards.append(op)
+                    op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+                    extra_guards.append(op)
+                    return
+                else:
+                    raise VirtualStatesCantMatch("other's class is unknown")
+            elif other.level == LEVEL_NONNULL:
+                if box and self.known_class.same_constant(cpu.ts.cls_of_box(box)):
+                    op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+                    extra_guards.append(op)
+                    return
+                else:
+                    raise VirtualStatesCantMatch("other's class is unknown")
+            elif other.level == LEVEL_KNOWNCLASS:
+                if self.known_class.same_constant(other.known_class):
+                    return
+                raise VirtualStatesCantMatch("classes don't match")
+            else:
+                assert other.level == LEVEL_CONSTANT
+                if (other.constbox.nonnull() and
+                        self.known_class.same_constant(cpu.ts.cls_of_box(other.constbox))):
+                    return
+                else:
+                    raise VirtualStatesCantMatch("classes don't match")
 
-        if not self.intbound.contains_bound(other.intbound):
-            return False
-        if self.lenbound and other.lenbound:
-            if self.lenbound.mode != other.lenbound.mode or \
-               self.lenbound.descr != other.lenbound.descr or \
-               not self.lenbound.bound.contains_bound(other.lenbound.bound):
-                return False
-        elif self.lenbound:
-            return False
-        return True
+        else:
+            assert self.level == LEVEL_CONSTANT
+            if other.level == LEVEL_CONSTANT:
+                if self.constbox.same_constant(other.constbox):
+                    return
+                raise VirtualStatesCantMatch("different constants")
+            if box is not None and self.constbox.same_constant(box.constbox()):
+                op = ResOperation(rop.GUARD_VALUE, [box, self.constbox], None)
+                extra_guards.append(op)
+                return
+            else:
+                raise VirtualStatesCantMatch("other not constant")
+        assert 0, "unreachable"
 
-    def _generate_guards(self, other, box, cpu, extra_guards):
-        if not isinstance(other, NotVirtualStateInfo):
-            raise InvalidLoop('The VirtualStates does not match as a ' +
-                              'virtual appears where a pointer is needed ' +
-                              'and it is too late to force it.')
-
-        if self.lenbound or other.lenbound:
-            raise InvalidLoop('The array length bounds does not match.')
-
-        if self.is_opaque:
-            raise InvalidLoop('Generating guards for opaque pointers is not safe')
-
-        if self.level == LEVEL_KNOWNCLASS and \
-           box.nonnull() and \
-           self.known_class.same_constant(cpu.ts.cls_of_box(box)):
-            # Note: This is only a hint on what the class of box was
-            # during the trace. There are actually no guarentees that this
-            # box realy comes from a trace. The hint is used here to choose
-            # between either eimtting a guard_class and jumping to an
-            # excisting compiled loop or retracing the loop. Both
-            # alternatives will always generate correct behaviour, but
-            # performace will differ.
-            op = ResOperation(rop.GUARD_NONNULL, [box], None)
-            extra_guards.append(op)
-            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
-            extra_guards.append(op)
+    def _generate_guards_intbounds(self, other, box, extra_guards):
+        if self.intbound.contains_bound(other.intbound):
             return
-
-        if (self.level == LEVEL_NONNULL and
-               other.level == LEVEL_UNKNOWN and
-               isinstance(box, BoxPtr) and
-               box.nonnull()):
-            op = ResOperation(rop.GUARD_NONNULL, [box], None)
-            extra_guards.append(op)
+        if (box is not None and isinstance(box, BoxInt) and
+                self.intbound.contains(box.getint())):
+            # this may generate a few more guards than needed, but they are
+            # optimized away when emitting them
+            self.intbound.make_guards(box, extra_guards)
             return
-
-        if (self.level == LEVEL_UNKNOWN and
-               other.level == LEVEL_UNKNOWN and
-               isinstance(box, BoxInt) and
-               self.intbound.contains(box.getint())):
-            if self.intbound.has_lower:
-                bound = self.intbound.lower
-                if not (other.intbound.has_lower and
-                        other.intbound.lower >= bound):
-                    res = BoxInt()
-                    op = ResOperation(rop.INT_GE, [box, ConstInt(bound)], res)
-                    extra_guards.append(op)
-                    op = ResOperation(rop.GUARD_TRUE, [res], None)
-                    extra_guards.append(op)
-            if self.intbound.has_upper:
-                bound = self.intbound.upper
-                if not (other.intbound.has_upper and
-                        other.intbound.upper <= bound):
-                    res = BoxInt()
-                    op = ResOperation(rop.INT_LE, [box, ConstInt(bound)], res)
-                    extra_guards.append(op)
-                    op = ResOperation(rop.GUARD_TRUE, [res], None)
-                    extra_guards.append(op)
-            return
-
-        # Remaining cases are probably not interesting
-        raise InvalidLoop('Generating guards for making the VirtualStates ' +
-                          'at hand match have not been implemented')
+        raise VirtualStatesCantMatch("intbounds don't match")
 
     def enum_forced_boxes(self, boxes, value, optimizer):
         if self.level == LEVEL_CONSTANT:
@@ -363,25 +410,37 @@
     def _enum(self, virtual_state):
         if self.level == LEVEL_CONSTANT:
             return
-        self.position_in_notvirtuals = len(virtual_state.notvirtuals)
-        virtual_state.notvirtuals.append(self)
+        self.position_in_notvirtuals = virtual_state.numnotvirtuals
+        virtual_state.numnotvirtuals += 1
 
-    def debug_print(self, indent, seen, bad):
+    def debug_print(self, indent, seen, bad, metainterp_sd=None):
         mark = ''
         if self in bad:
             mark = '*'
-        if we_are_translated():
-            l = {LEVEL_UNKNOWN: 'Unknown',
-                 LEVEL_NONNULL: 'NonNull',
-                 LEVEL_KNOWNCLASS: 'KnownClass',
-                 LEVEL_CONSTANT: 'Constant',
-                 }[self.level]
+        if self.level == LEVEL_UNKNOWN:


More information about the pypy-commit mailing list