[pypy-commit] pypy default: merge optinfo-into-bridges-3

cfbolz pypy.commits at gmail.com
Sat Feb 25 15:54:38 EST 2017


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: 
Changeset: r90358:e2a82bc8b3d4
Date: 2017-02-25 21:53 +0100
http://bitbucket.org/pypy/pypy/changeset/e2a82bc8b3d4/

Log:	merge optinfo-into-bridges-3

	Improve the optimization of branchy Python code by retaining more
	information across failing guards. This is done by appending some
	carefully encoded extra information into the resume code. So far the
	following information is encoded:

	- which boxes have known classes
	- known fields of instances

	The branch also does some cleanup in resume.py.

	Thanks fijal for the review.

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
@@ -143,6 +143,13 @@
 behavior is also available on Python 2.x or for the ``dict`` type by
 calling ``__pypy__.move_to_end(dict, key, last=True)``.
 
+
+.. branch optinfo-into-bridges-3
+
+Improve the optimization of branchy Python code by retaining more information
+across failing guards.
+
+
 .. branch: space-newtext
 
 Internal refactoring of ``space.wrap()``, which is now replaced with
diff --git a/rpython/jit/codewriter/jitcode.py b/rpython/jit/codewriter/jitcode.py
--- a/rpython/jit/codewriter/jitcode.py
+++ b/rpython/jit/codewriter/jitcode.py
@@ -146,14 +146,13 @@
     def get_register_index_f(self, index):
         return ord(self.live_f[index])
 
-    def enumerate_vars(self, callback_i, callback_r, callback_f, spec, index):
+    def enumerate_vars(self, callback_i, callback_r, callback_f, spec):
         for i in range(self.get_register_count_i()):
-            index = callback_i(index, self.get_register_index_i(i))
+            callback_i(self.get_register_index_i(i))
         for i in range(self.get_register_count_r()):
-            index = callback_r(index, self.get_register_index_r(i))
+            callback_r(self.get_register_index_r(i))
         for i in range(self.get_register_count_f()):
-            index = callback_f(index, self.get_register_index_f(i))
-        return index
+            callback_f(self.get_register_index_f(i))
     enumerate_vars._annspecialcase_ = 'specialize:arg(4)'
 
 _liveness_cache = {}
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
@@ -85,13 +85,14 @@
     """ This represents ops() with a jump at the end that goes to some
     loop, we need to deal with virtual state and inlining of short preamble
     """
-    def __init__(self, trace, runtime_boxes, call_pure_results=None,
+    def __init__(self, trace, runtime_boxes, resumestorage=None, call_pure_results=None,
                  enable_opts=None, inline_short_preamble=False):
         self.trace = trace
         self.runtime_boxes = runtime_boxes
         self.call_pure_results = call_pure_results
         self.enable_opts = enable_opts
         self.inline_short_preamble = inline_short_preamble
+        self.resumestorage = resumestorage
 
     def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll):
         from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer
@@ -100,7 +101,8 @@
         return opt.optimize_bridge(self.trace, self.runtime_boxes,
                                    self.call_pure_results,
                                    self.inline_short_preamble,
-                                   self.box_names_memo)
+                                   self.box_names_memo,
+                                   self.resumestorage)
 
 class UnrolledLoopData(CompileData):
     """ This represents label() ops jump with extra info that's from the
@@ -870,10 +872,9 @@
 
 
 class ResumeGuardDescr(AbstractResumeGuardDescr):
-    _attrs_ = ('rd_numb', 'rd_count', 'rd_consts', 'rd_virtuals',
+    _attrs_ = ('rd_numb', 'rd_consts', 'rd_virtuals',
                'rd_pendingfields', 'status')
     rd_numb = lltype.nullptr(NUMBERING)
-    rd_count = 0
     rd_consts = None
     rd_virtuals = None
     rd_pendingfields = lltype.nullptr(PENDINGFIELDSP.TO)
@@ -882,7 +883,6 @@
         if isinstance(other, ResumeGuardCopiedDescr):
             other = other.prev
         assert isinstance(other, ResumeGuardDescr)
-        self.rd_count = other.rd_count
         self.rd_consts = other.rd_consts
         self.rd_pendingfields = other.rd_pendingfields
         self.rd_virtuals = other.rd_virtuals
@@ -895,7 +895,6 @@
 
     def store_final_boxes(self, guard_op, boxes, metainterp_sd):
         guard_op.setfailargs(boxes)
-        self.rd_count = len(boxes)
         self.store_hash(metainterp_sd)
 
     def clone(self):
@@ -1077,7 +1076,15 @@
     call_pure_results = metainterp.call_pure_results
 
     if metainterp.history.ends_with_jump:
-        data = BridgeCompileData(trace, runtime_boxes,
+        if isinstance(resumekey, ResumeGuardCopiedDescr):
+            key = resumekey.prev
+            assert isinstance(key, ResumeGuardDescr)
+        elif isinstance(resumekey, ResumeFromInterpDescr):
+            key = None
+        else:
+            key = resumekey
+            assert isinstance(key, ResumeGuardDescr)
+        data = BridgeCompileData(trace, runtime_boxes, key,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts,
                                  inline_short_preamble=inline_short_preamble)
diff --git a/rpython/jit/metainterp/opencoder.py b/rpython/jit/metainterp/opencoder.py
--- a/rpython/jit/metainterp/opencoder.py
+++ b/rpython/jit/metainterp/opencoder.py
@@ -66,7 +66,7 @@
         assert isinstance(snapshot, TopSnapshot)
         self.vable_array = snapshot.vable_array
         self.vref_array = snapshot.vref_array
-        self.size = len(self.vable_array) + len(self.vref_array) + 2
+        self.size = len(self.vable_array) + len(self.vref_array) + 3
         jc_index, pc = unpack_uint(snapshot.packed_jitcode_pc)
         self.framestack = []
         if jc_index == 2**16-1:
diff --git a/rpython/jit/metainterp/optimizeopt/bridgeopt.py b/rpython/jit/metainterp/optimizeopt/bridgeopt.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/optimizeopt/bridgeopt.py
@@ -0,0 +1,135 @@
+""" Code to feed information from the optimizer via the resume code into the
+optimizer of the bridge attached to a guard. """
+
+from rpython.jit.metainterp import resumecode
+
+
+# adds the following sections at the end of the resume code:
+#
+# ---- known classes
+# <bitfield> size is the number of reference boxes in the liveboxes
+#            1 klass known
+#            0 klass unknown
+#            (the class is found by actually looking at the runtime value)
+#            the bits are bunched in bunches of 7
+#
+# ---- heap knowledge
+# <length>
+# (<box1> <descr> <box2>) length times, if getfield(box1, descr) == box2
+#                         both boxes should be in the liveboxes
+#
+# ----
+
+
+# maybe should be delegated to the optimization classes?
+
+def tag_box(box, liveboxes_from_env, memo):
+    from rpython.jit.metainterp.history import Const
+    if isinstance(box, Const):
+        return memo.getconst(box)
+    else:
+        return liveboxes_from_env[box] # has to exist
+
+def decode_box(resumestorage, tagged, liveboxes, cpu):
+    from rpython.jit.metainterp.resume import untag, TAGCONST, TAGINT, TAGBOX
+    from rpython.jit.metainterp.resume import NULLREF, TAG_CONST_OFFSET, tagged_eq
+    from rpython.jit.metainterp.history import ConstInt
+    num, tag = untag(tagged)
+    # NB: the TAGVIRTUAL case can't happen here, because this code runs after
+    # virtuals are already forced again
+    if tag == TAGCONST:
+        if tagged_eq(tagged, NULLREF):
+            box = cpu.ts.CONST_NULL
+        else:
+            box = resumestorage.rd_consts[num - TAG_CONST_OFFSET]
+    elif tag == TAGINT:
+        box = ConstInt(num)
+    elif tag == TAGBOX:
+        box = liveboxes[num]
+    else:
+        raise AssertionError("unreachable")
+    return box
+
+def serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, liveboxes_from_env, memo):
+    available_boxes = {}
+    for box in liveboxes:
+        if box is not None and box in liveboxes_from_env:
+            available_boxes[box] = None
+    metainterp_sd = optimizer.metainterp_sd
+
+    # class knowledge is stored as bits, true meaning the class is known, false
+    # means unknown. on deserializing we look at the bits, and read the runtime
+    # class for the known classes (which has to be the same in the bridge) and
+    # mark that as known. this works for guard_class too: the class is only
+    # known *after* the guard
+    bitfield = 0
+    shifts = 0
+    for box in liveboxes:
+        if box is None or box.type != "r":
+            continue
+        info = optimizer.getptrinfo(box)
+        known_class = info is not None and info.get_known_class(optimizer.cpu) is not None
+        bitfield <<= 1
+        bitfield |= known_class
+        shifts += 1
+        if shifts == 6:
+            numb_state.append_int(bitfield)
+            bitfield = shifts = 0
+    if shifts:
+        numb_state.append_int(bitfield << (6 - shifts))
+
+    # heap knowledge: we store triples of known heap fields in non-virtual
+    # structs
+    # XXX could be extended to arrays
+    if optimizer.optheap:
+        triples = optimizer.optheap.serialize_optheap(available_boxes)
+        # can only encode descrs that have a known index into
+        # metainterp_sd.all_descrs
+        triples = [triple for triple in triples if triple[1].descr_index != -1]
+        numb_state.append_int(len(triples))
+        for box1, descr, box2 in triples:
+            index = descr.descr_index
+            numb_state.append_short(tag_box(box1, liveboxes_from_env, memo))
+            numb_state.append_int(index)
+            numb_state.append_short(tag_box(box2, liveboxes_from_env, memo))
+    else:
+        numb_state.append_int(0)
+
+def deserialize_optimizer_knowledge(optimizer, resumestorage, frontend_boxes, liveboxes):
+    reader = resumecode.Reader(resumestorage.rd_numb)
+    assert len(frontend_boxes) == len(liveboxes)
+    metainterp_sd = optimizer.metainterp_sd
+
+    # skip resume section
+    startcount = reader.next_item()
+    reader.jump(startcount - 1)
+
+    # class knowledge
+    bitfield = 0
+    mask = 0
+    for i, box in enumerate(liveboxes):
+        if box.type != "r":
+            continue
+        if not mask:
+            bitfield = reader.next_item()
+            mask = 0b100000
+        class_known = bitfield & mask
+        mask >>= 1
+        if class_known:
+            cls = optimizer.cpu.ts.cls_of_box(frontend_boxes[i])
+            optimizer.make_constant_class(box, cls)
+
+    # heap knowledge
+    if not optimizer.optheap:
+        return
+    length = reader.next_item()
+    result = []
+    for i in range(length):
+        tagged = reader.next_item()
+        box1 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu)
+        index = reader.next_item()
+        descr = metainterp_sd.all_descrs[index]
+        tagged = reader.next_item()
+        box2 = decode_box(resumestorage, tagged, liveboxes, metainterp_sd.cpu)
+        result.append((box1, descr, box2))
+    optimizer.optheap.deserialize_optheap(result)
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -48,6 +48,7 @@
         # that has a non-None entry at
         # info._fields[descr.get_index()]
         # must be in cache_infos
+        assert structop.type == 'r'
         self.cached_structs.append(structop)
         self.cached_infos.append(info)
 
@@ -693,6 +694,36 @@
         self._seen_guard_not_invalidated = True
         return self.emit(op)
 
+    def serialize_optheap(self, available_boxes):
+        result = []
+        for descr, cf in self.cached_fields.iteritems():
+            if cf._lazy_set:
+                continue # XXX safe default for now
+            parent_descr = descr.get_parent_descr()
+            if not parent_descr.is_object():
+                continue # XXX could be extended to non-instance objects
+            for i, box1 in enumerate(cf.cached_structs):
+                if box1 not in available_boxes:
+                    continue
+                structinfo = cf.cached_infos[i]
+                box2 = structinfo.getfield(descr).get_box_replacement()
+                if isinstance(box2, Const) or box2 in available_boxes:
+                    result.append((box1, descr, box2))
+        return result
+
+    def deserialize_optheap(self, triples):
+        for box1, descr, box2 in triples:
+            parent_descr = descr.get_parent_descr()
+            assert parent_descr.is_object()
+            structinfo = box1.get_forwarded()
+            if not isinstance(structinfo, info.AbstractVirtualPtrInfo):
+                structinfo = info.InstancePtrInfo(parent_descr)
+                structinfo.init_fields(parent_descr, descr.get_index())
+                box1.set_forwarded(structinfo)
+
+            cf = self.field_cache(descr)
+            structinfo.setfield(descr, box1, box2, optheap=self, cf=cf)
+
 
 dispatch_opt = make_dispatcher_method(OptHeap, 'optimize_',
                                       default=OptHeap.emit)
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
@@ -297,6 +297,7 @@
         self.optrewrite = None
         self.optearlyforce = None
         self.optunroll = None
+        self._really_emitted_operation = None
 
         self._last_guard_op = None
 
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizebridge.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizebridge.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizebridge.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizebridge.py
@@ -30,6 +30,7 @@
         self.add_guard_future_condition(bridge)
         trace = oparser.convert_loop_to_trace(bridge, FakeMetaInterpStaticData(self.cpu))
         data = compile.BridgeCompileData(trace, self.convert_values(bridge.operations[-1].getarglist(), bridge_values),
+                                         None,
                                          enable_opts=self.enable_opts,
                             inline_short_preamble=inline_short_preamble)
         bridge_info, ops = self._do_optimize_loop(data)
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
@@ -232,9 +232,15 @@
         return label_vs
 
     def optimize_bridge(self, trace, runtime_boxes, call_pure_results,
-                        inline_short_preamble, box_names_memo):
+                        inline_short_preamble, box_names_memo, resumestorage):
+        from rpython.jit.metainterp.optimizeopt.bridgeopt import deserialize_optimizer_knowledge
+        frontend_inputargs = trace.inputargs
         trace = trace.get_iter()
         self._check_no_forwarding([trace.inputargs])
+        if resumestorage:
+            deserialize_optimizer_knowledge(self.optimizer,
+                                            resumestorage, frontend_inputargs,
+                                            trace.inputargs)
         info, ops = self.optimizer.propagate_all_forward(trace,
             call_pure_results, False)
         jump_op = info.jump_op
diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py
--- a/rpython/jit/metainterp/resoperation.py
+++ b/rpython/jit/metainterp/resoperation.py
@@ -752,7 +752,6 @@
 
     def __init__(self, r=lltype.nullptr(llmemory.GCREF.TO)):
         self.setref_base(r)
-        self.datatype = 'r'
 
     def reset_value(self):
         self.setref_base(lltype.nullptr(llmemory.GCREF.TO))
diff --git a/rpython/jit/metainterp/resume.py b/rpython/jit/metainterp/resume.py
--- a/rpython/jit/metainterp/resume.py
+++ b/rpython/jit/metainterp/resume.py
@@ -162,25 +162,13 @@
 UNINITIALIZED = tag(-2, TAGCONST)   # used for uninitialized string characters
 TAG_CONST_OFFSET = 0
 
-class NumberingState(object):
+class NumberingState(resumecode.Writer):
     def __init__(self, size):
+        resumecode.Writer.__init__(self, size)
         self.liveboxes = {}
-        self.current = [rffi.cast(rffi.SHORT, 0)] * size
-        self._pos = 0
         self.num_boxes = 0
         self.num_virtuals = 0
 
-    def append_short(self, item):
-        self.current[self._pos] = item
-        self._pos += 1
-
-    def append_int(self, item):
-        short = rffi.cast(rffi.SHORT, item)
-        assert rffi.cast(lltype.Signed, short) == item
-        return self.append_short(short)
-
-    def create_numbering(self):
-        return resumecode.create_numbering(self.current)
 
 class ResumeDataLoopMemo(object):
 
@@ -268,6 +256,8 @@
     def number(self, optimizer, position, trace):
         snapshot_iter = trace.get_snapshot_iter(position)
         numb_state = NumberingState(snapshot_iter.size)
+        numb_state.append_int(0) # patch later: size of resume section
+        numb_state.append_int(0) # patch later: number of failargs
 
         arr = snapshot_iter.vable_array
 
@@ -287,6 +277,7 @@
             numb_state.append_int(pc)
             self._number_boxes(
                     snapshot_iter, snapshot.box_array, optimizer, numb_state)
+        numb_state.patch_current_size(0)
 
         return numb_state
 
@@ -471,6 +462,9 @@
         self._number_virtuals(liveboxes, optimizer, num_virtuals)
         self._add_pending_fields(optimizer, pending_setfields)
 
+        numb_state.patch(1, len(liveboxes))
+
+        self._add_optimizer_sections(numb_state, liveboxes, liveboxes_from_env)
         storage.rd_numb = numb_state.create_numbering()
         storage.rd_consts = self.memo.consts
         return liveboxes[:]
@@ -590,6 +584,12 @@
                 return self.liveboxes_from_env[box]
             return self.liveboxes[box]
 
+    def _add_optimizer_sections(self, numb_state, liveboxes, liveboxes_from_env):
+        # add extra information about things the optimizer learned
+        from rpython.jit.metainterp.optimizeopt.bridgeopt import serialize_optimizer_knowledge
+        serialize_optimizer_knowledge(
+            self.optimizer, numb_state, liveboxes, liveboxes_from_env, self.memo)
+
 class AbstractVirtualInfo(object):
     kind = REF
     is_about_raw = False
@@ -931,9 +931,10 @@
 
     def _init(self, cpu, storage):
         self.cpu = cpu
-        self.numb = storage.rd_numb
-        self.cur_index = 0
-        self.count = storage.rd_count
+        self.resumecodereader = resumecode.Reader(storage.rd_numb)
+        items_resume_section = self.resumecodereader.next_item()
+        self.items_resume_section = items_resume_section
+        self.count = self.resumecodereader.next_item()
         self.consts = storage.rd_consts
 
     def _prepare(self, storage):
@@ -941,14 +942,21 @@
         self._prepare_pendingfields(storage.rd_pendingfields)
 
     def read_jitcode_pos_pc(self):
-        jitcode_pos, self.cur_index = resumecode.numb_next_item(self.numb,
-            self.cur_index)
-        pc, self.cur_index = resumecode.numb_next_item(self.numb,
-            self.cur_index)
+        jitcode_pos = self.resumecodereader.next_item()
+        pc = self.resumecodereader.next_item()
         return jitcode_pos, pc
 
+    def next_int(self):
+        return self.decode_int(self.resumecodereader.next_item())
+
+    def next_ref(self):
+        return self.decode_ref(self.resumecodereader.next_item())
+
+    def next_float(self):
+        return self.decode_float(self.resumecodereader.next_item())
+
     def done_reading(self):
-        return self.cur_index >= len(self.numb.code)
+        return self.resumecodereader.items_read >= self.items_resume_section
 
     def getvirtual_ptr(self, index):
         # Returns the index'th virtual, building it lazily if needed.
@@ -1025,29 +1033,22 @@
     def _prepare_next_section(self, info):
         # Use info.enumerate_vars(), normally dispatching to
         # rpython.jit.codewriter.jitcode.  Some tests give a different 'info'.
-        self.cur_index = info.enumerate_vars(self._callback_i,
-                                        self._callback_r,
-                                        self._callback_f,
-                                        self.unique_id,  # <-- annotation hack
-                                        self.cur_index)
+        info.enumerate_vars(self._callback_i,
+                            self._callback_r,
+                            self._callback_f,
+                            self.unique_id)  # <-- annotation hack
 
-    def _callback_i(self, index, register_index):
-        item, index = resumecode.numb_next_item(self.numb, index)
-        value = self.decode_int(item)
+    def _callback_i(self, register_index):
+        value = self.next_int()
         self.write_an_int(register_index, value)
-        return index
 
-    def _callback_r(self, index, register_index):
-        item, index = resumecode.numb_next_item(self.numb, index)
-        value = self.decode_ref(item)
+    def _callback_r(self, register_index):
+        value = self.next_ref()
         self.write_a_ref(register_index, value)
-        return index
 
-    def _callback_f(self, index, register_index):
-        item, index = resumecode.numb_next_item(self.numb, index)
-        value = self.decode_float(item)
+    def _callback_f(self, register_index):
+        value = self.next_float()
         self.write_a_float(register_index, value)
-        return index
 
 # ---------- when resuming for pyjitpl.py, make boxes ----------
 
@@ -1057,6 +1058,7 @@
     boxes = resumereader.consume_vref_and_vable_boxes(virtualizable_info,
                                                       greenfield_info)
     virtualizable_boxes, virtualref_boxes = boxes
+
     while not resumereader.done_reading():
         jitcode_pos, pc = resumereader.read_jitcode_pos_pc()
         jitcode = metainterp.staticdata.jitcodes[jitcode_pos]
@@ -1076,7 +1078,7 @@
         self._init(metainterp.cpu, storage)
         self.deadframe = deadframe
         self.metainterp = metainterp
-        self.liveboxes = [None] * storage.rd_count
+        self.liveboxes = [None] * self.count
         self._prepare(storage)
 
     def consume_boxes(self, info, boxes_i, boxes_r, boxes_f):
@@ -1085,42 +1087,30 @@
         self.boxes_f = boxes_f
         self._prepare_next_section(info)
 
-    def consume_virtualizable_boxes(self, vinfo, index):
+    def consume_virtualizable_boxes(self, vinfo):
         # we have to ignore the initial part of 'nums' (containing vrefs),
         # find the virtualizable from nums[-1], and use it to know how many
         # boxes of which type we have to return.  This does not write
         # anything into the virtualizable.
-        numb = self.numb
-        item, index = resumecode.numb_next_item(numb, index)
-        virtualizablebox = self.decode_ref(item)
+        virtualizablebox = self.next_ref()
         virtualizable = vinfo.unwrap_virtualizable_box(virtualizablebox)
-        return vinfo.load_list_of_boxes(virtualizable, self, virtualizablebox,
-            numb, index)
+        return vinfo.load_list_of_boxes(virtualizable, self, virtualizablebox)
 
-    def consume_virtualref_boxes(self, index):
+    def consume_virtualref_boxes(self):
         # Returns a list of boxes, assumed to be all BoxPtrs.
         # We leave up to the caller to call vrefinfo.continue_tracing().
-        size, index = resumecode.numb_next_item(self.numb, index)
-        if size == 0:
-            return [], index
-        lst = []
-        for i in range(size * 2):
-            item, index = resumecode.numb_next_item(self.numb, index)
-            lst.append(self.decode_ref(item))
-        return lst, index
+        size = self.resumecodereader.next_item()
+        return [self.next_ref() for i in range(size * 2)]
 
     def consume_vref_and_vable_boxes(self, vinfo, ginfo):
-        vable_size, index = resumecode.numb_next_item(self.numb, 0)
+        vable_size = self.resumecodereader.next_item()
         if vinfo is not None:
-            virtualizable_boxes, index = self.consume_virtualizable_boxes(vinfo,
-                                                                          index)
+            virtualizable_boxes = self.consume_virtualizable_boxes(vinfo)
         elif ginfo is not None:
-            item, index = resumecode.numb_next_item(self.numb, index)
-            virtualizable_boxes = [self.decode_ref(item)]
+            virtualizable_boxes = [self.next_ref()]
         else:
             virtualizable_boxes = None
-        virtualref_boxes, index = self.consume_virtualref_boxes(index)
-        self.cur_index = index
+        virtualref_boxes = self.consume_virtualref_boxes()
         return virtualizable_boxes, virtualref_boxes
 
     def allocate_with_vtable(self, descr=None):
@@ -1297,7 +1287,7 @@
         self.liveboxes[num] = box
         return box
 
-    def decode_box_of_type(self, TYPE, tagged):
+    def next_box_of_type(self, TYPE):
         kind = getkind(TYPE)
         if kind == 'int':
             kind = INT
@@ -1307,8 +1297,8 @@
             kind = FLOAT
         else:
             raise AssertionError(kind)
-        return self.decode_box(tagged, kind)
-    decode_box_of_type._annspecialcase_ = 'specialize:arg(1)'
+        return self.decode_box(self.resumecodereader.next_item(), kind)
+    next_box_of_type._annspecialcase_ = 'specialize:arg(1)'
 
     def write_an_int(self, index, box):
         self.boxes_i[index] = box
@@ -1397,64 +1387,54 @@
         info = blackholeinterp.get_current_position_info()
         self._prepare_next_section(info)
 
-    def consume_virtualref_info(self, vrefinfo, index):
+    def consume_virtualref_info(self, vrefinfo):
         # we have to decode a list of references containing pairs
         # [..., virtual, vref, ...] and returns the index at the end
-        size, index = resumecode.numb_next_item(self.numb, index)
+        size = self.resumecodereader.next_item()
         if vrefinfo is None or size == 0:
             assert size == 0
-            return index
+            return
         for i in range(size):
-            virtual_item, index = resumecode.numb_next_item(
-                self.numb, index)
-            vref_item, index = resumecode.numb_next_item(
-                self.numb, index)
-            virtual = self.decode_ref(virtual_item)
-            vref = self.decode_ref(vref_item)
+            virtual = self.next_ref()
+            vref = self.next_ref()
             # For each pair, we store the virtual inside the vref.
             vrefinfo.continue_tracing(vref, virtual)
-        return index
 
-    def consume_vable_info(self, vinfo, index):
+    def consume_vable_info(self, vinfo):
         # we have to ignore the initial part of 'nums' (containing vrefs),
         # find the virtualizable from nums[-1], load all other values
         # from the CPU stack, and copy them into the virtualizable
-        numb = self.numb
-        item, index = resumecode.numb_next_item(self.numb, index)
-        virtualizable = self.decode_ref(item)
+        virtualizable = self.next_ref()
         # just reset the token, we'll force it later
         vinfo.reset_token_gcref(virtualizable)
-        index = vinfo.write_from_resume_data_partial(virtualizable, self,
-            index, numb)
-        return index
+        vinfo.write_from_resume_data_partial(virtualizable, self)
 
-    def load_value_of_type(self, TYPE, tagged):
+    def load_next_value_of_type(self, TYPE):
         from rpython.jit.metainterp.warmstate import specialize_value
         kind = getkind(TYPE)
         if kind == 'int':
-            x = self.decode_int(tagged)
+            x = self.next_int()
         elif kind == 'ref':
-            x = self.decode_ref(tagged)
+            x = self.next_ref()
         elif kind == 'float':
-            x = self.decode_float(tagged)
+            x = self.next_float()
         else:
             raise AssertionError(kind)
         return specialize_value(TYPE, x)
-    load_value_of_type._annspecialcase_ = 'specialize:arg(1)'
+    load_next_value_of_type._annspecialcase_ = 'specialize:arg(1)'
 
     def consume_vref_and_vable(self, vrefinfo, vinfo, ginfo):
-        vable_size, index = resumecode.numb_next_item(self.numb, 0)
+        vable_size = self.resumecodereader.next_item()
         if self.resume_after_guard_not_forced != 2:
             if vinfo is not None:
-                index = self.consume_vable_info(vinfo, index)
+                self.consume_vable_info(vinfo)
             if ginfo is not None:
-                _, index = resumecode.numb_next_item(self.numb, index)
-            index = self.consume_virtualref_info(vrefinfo, index)
+                _ = self.resumecodereader.next_item()
+            self.consume_virtualref_info(vrefinfo)
         else:
-            index = resumecode.numb_next_n_items(self.numb, vable_size, index)
-            vref_size, index = resumecode.numb_next_item(self.numb, index)
-            index = resumecode.numb_next_n_items(self.numb, vref_size * 2, index)
-        self.cur_index = index 
+            self.resumecodereader.jump(vable_size)
+            vref_size = self.resumecodereader.next_item()
+            self.resumecodereader.jump(vref_size * 2)
 
     def allocate_with_vtable(self, descr=None):
         from rpython.jit.metainterp.executor import exec_new_with_vtable
diff --git a/rpython/jit/metainterp/resumecode.py b/rpython/jit/metainterp/resumecode.py
--- a/rpython/jit/metainterp/resumecode.py
+++ b/rpython/jit/metainterp/resumecode.py
@@ -1,6 +1,9 @@
 
 """ Resume bytecode. It goes as following:
 
+  # ----- resume section
+  [total size of resume section]
+  [number of failargs]
   [<length> <virtualizable object> <numb> <numb> <numb>]    if vinfo is not None
    -OR-
   [1 <ginfo object>]                                        if ginfo is not None
@@ -13,10 +16,14 @@
   [<pc> <jitcode> <numb> <numb>]
   ...
 
-  until the length of the array.
+  until the size of the resume section
+
+  # ----- optimization section
+  <more code>                                      further sections according to bridgeopt.py
 """
 
 from rpython.rtyper.lltypesystem import rffi, lltype
+from rpython.rlib import objectmodel
 
 NUMBERINGP = lltype.Ptr(lltype.GcForwardReference())
 NUMBERING = lltype.GcStruct('Numbering',
@@ -24,33 +31,24 @@
 NUMBERINGP.TO.become(NUMBERING)
 NULL_NUMBER = lltype.nullptr(NUMBERING)
 
-def create_numbering(lst, total=-1):
-    if total == -1:
-        total = len(lst)
-    result = []
-    for i in range(total):
-        item = lst[i]
-        item = rffi.cast(lltype.Signed, item)
-        item *= 2
-        if item < 0:
-            item = -1 - item
+def append_numbering(lst, item):
+    item = rffi.cast(lltype.Signed, item)
+    item *= 2
+    if item < 0:
+        item = -1 - item
 
-        assert item >= 0
-        if item < 2**7:
-            result.append(rffi.cast(rffi.UCHAR, item))
-        elif item < 2**14:
-            result.append(rffi.cast(rffi.UCHAR, item | 0x80))
-            result.append(rffi.cast(rffi.UCHAR, item >> 7))
-        else:
-            assert item < 2**16
-            result.append(rffi.cast(rffi.UCHAR, item | 0x80))
-            result.append(rffi.cast(rffi.UCHAR, (item >> 7) | 0x80))
-            result.append(rffi.cast(rffi.UCHAR, item >> 14))
+    assert item >= 0
+    if item < 2**7:
+        lst.append(rffi.cast(rffi.UCHAR, item))
+    elif item < 2**14:
+        lst.append(rffi.cast(rffi.UCHAR, item | 0x80))
+        lst.append(rffi.cast(rffi.UCHAR, item >> 7))
+    else:
+        assert item < 2**16
+        lst.append(rffi.cast(rffi.UCHAR, item | 0x80))
+        lst.append(rffi.cast(rffi.UCHAR, (item >> 7) | 0x80))
+        lst.append(rffi.cast(rffi.UCHAR, item >> 14))
 
-    numb = lltype.malloc(NUMBERING, len(result))
-    for i in range(len(result)):
-        numb.code[i] = result[i]
-    return numb
 
 def numb_next_item(numb, index):
     value = rffi.cast(lltype.Signed, numb.code[index])
@@ -81,3 +79,64 @@
         next, i = numb_next_item(numb, i)
         l.append(next)
     return l
+
+class Writer(object):
+    def __init__(self, size=0):
+        self.current = objectmodel.newlist_hint(size)
+
+    def append_short(self, item):
+        self.current.append(item)
+
+    def append_int(self, item):
+        short = rffi.cast(rffi.SHORT, item)
+        assert rffi.cast(lltype.Signed, short) == item
+        return self.append_short(short)
+
+    def create_numbering(self):
+        final = objectmodel.newlist_hint(len(self.current) * 3)
+        for item in self.current:
+            append_numbering(final, item)
+        numb = lltype.malloc(NUMBERING, len(final))
+        for i, elt in enumerate(final):
+            numb.code[i] = elt
+        return numb
+
+    def patch_current_size(self, index):
+        self.patch(index, len(self.current))
+
+    def patch(self, index, item):
+        self.current[index] = item
+
+def create_numbering(l):
+    w = Writer()
+    for item in l:
+        w.append_int(item)
+    return w.create_numbering()
+
+
+class Reader(object):
+    def __init__(self, code):
+        self.code = code
+        self.cur_pos = 0 # index into the code
+        self.items_read = 0 # number of items read
+
+    def next_item(self):
+        result, self.cur_pos = numb_next_item(self.code, self.cur_pos)
+        self.items_read += 1
+        return result
+
+    def peek(self):
+        result, _ = numb_next_item(self.code, self.cur_pos)
+        return result
+
+    def jump(self, size):
+        """ jump n items forward without returning anything """
+        index = self.cur_pos
+        for i in range(size):
+            _, index = numb_next_item(self.code, index)
+        self.items_read += size
+        self.cur_pos = index
+
+    def unpack(self):
+        # mainly for debugging
+        return unpack_numbering(self.code)
diff --git a/rpython/jit/metainterp/test/test_bridgeopt.py b/rpython/jit/metainterp/test/test_bridgeopt.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/test/test_bridgeopt.py
@@ -0,0 +1,188 @@
+# tests that check that information is fed from the optimizer into the bridges
+
+import math
+from rpython.rlib import jit
+from rpython.jit.metainterp.test.support import LLJitMixin
+from rpython.jit.metainterp.optimizeopt.bridgeopt import serialize_optimizer_knowledge
+from rpython.jit.metainterp.optimizeopt.bridgeopt import deserialize_optimizer_knowledge
+from rpython.jit.metainterp.resoperation import InputArgRef, InputArgInt
+from rpython.jit.metainterp.resume import NumberingState
+from rpython.jit.metainterp.resumecode import unpack_numbering
+from rpython.jit.metainterp.optimizeopt.info import InstancePtrInfo
+
+from hypothesis import strategies, given
+
+class FakeTS(object):
+    def __init__(self, dct):
+        self.dct = dct
+
+    def cls_of_box(self, box):
+        return self.dct[box]
+
+
+class FakeCPU(object):
+    def __init__(self, dct):
+        self.ts = FakeTS(dct)
+
+class FakeOptimizer(object):
+    metainterp_sd = None
+    optheap = None
+
+    def __init__(self, dct={}, cpu=None):
+        self.dct = dct
+        self.constant_classes = {}
+        self.cpu = cpu
+
+    def getptrinfo(self, arg):
+        return self.dct.get(arg, None)
+
+    def make_constant_class(self, arg, cls):
+        self.constant_classes[arg] = cls
+
+class FakeClass(object):
+    pass
+
+class FakeStorage(object):
+    def __init__(self, numb):
+        self.rd_numb = numb
+
+def test_known_classes():
+    box1 = InputArgRef()
+    box2 = InputArgRef()
+    box3 = InputArgRef()
+
+    cls = FakeClass()
+    dct = {box1: InstancePtrInfo(known_class=cls)}
+    optimizer = FakeOptimizer(dct)
+
+    numb_state = NumberingState(4)
+    numb_state.append_int(1) # size of resume block
+    liveboxes = [InputArgInt(), box2, box1, box3]
+
+    serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, {}, None)
+
+    assert unpack_numbering(numb_state.create_numbering()) == [1, 0b010000, 0]
+
+    rbox1 = InputArgRef()
+    rbox2 = InputArgRef()
+    rbox3 = InputArgRef()
+    after_optimizer = FakeOptimizer(cpu=FakeCPU({rbox1: cls}))
+    deserialize_optimizer_knowledge(
+        after_optimizer, FakeStorage(numb_state.create_numbering()),
+        [InputArgInt(), rbox2, rbox1, rbox3], liveboxes)
+    assert box1 in after_optimizer.constant_classes
+    assert box2 not in after_optimizer.constant_classes
+    assert box3 not in after_optimizer.constant_classes
+
+
+box_strategy = strategies.builds(InputArgInt) | strategies.builds(InputArgRef)
+tuples = strategies.tuples(box_strategy, strategies.booleans()).filter(
+        lambda (box, known_class): isinstance(box, InputArgRef) or not known_class)
+boxes_known_classes = strategies.lists(tuples, min_size=1)
+
+ at given(boxes_known_classes)
+def test_random_class_knowledge(boxes_known_classes):
+    cls = FakeClass()
+    dct1 = {box: InstancePtrInfo(known_class=cls)
+              for box, known_class in boxes_known_classes
+                  if known_class}
+    optimizer = FakeOptimizer(dct1)
+
+    refboxes = [box for (box, _) in boxes_known_classes
+                    if isinstance(box, InputArgRef)]
+
+    numb_state = NumberingState(1)
+    numb_state.append_int(1) # size of resume block
+    liveboxes = [box for (box, _) in boxes_known_classes]
+
+    serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, {}, None)
+
+    assert len(numb_state.create_numbering().code) == 2 + math.ceil(len(refboxes) / 6.0)
+
+    dct = {box: cls
+              for box, known_class in boxes_known_classes
+                  if known_class}
+    after_optimizer = FakeOptimizer(cpu=FakeCPU(dct))
+    deserialize_optimizer_knowledge(
+        after_optimizer, FakeStorage(numb_state.create_numbering()),
+        liveboxes, liveboxes)
+    for box, known_class in boxes_known_classes:
+        assert (box in after_optimizer.constant_classes) == known_class
+
+class TestOptBridge(LLJitMixin):
+    # integration tests
+    def test_bridge_guard_class(self):
+        myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n', 'a'])
+        class A(object):
+            def f(self):
+                return 1
+        class B(A):
+            def f(self):
+                return 2
+        def f(x, y, n):
+            if x:
+                a = A()
+            else:
+                a = B()
+            a.x = 0
+            res = 0
+            while y > 0:
+                myjitdriver.jit_merge_point(y=y, n=n, res=res, a=a)
+                res += a.f()
+                a.x += 1
+                if y > n:
+                    res += 1
+                res += a.f()
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 32, 16])
+        assert res == f(6, 32, 16)
+        self.check_trace_count(3)
+        self.check_resops(guard_class=1)
+
+    def test_bridge_field_read(self):
+        myjitdriver = jit.JitDriver(greens=[], reds=['y', 'res', 'n', 'a'])
+        class A(object):
+            def f(self):
+                return 1
+        class B(A):
+            def f(self):
+                return 2
+        class M(object):
+            _immutable_fields_ = ['x']
+            def __init__(self, x):
+                self.x = x
+
+        m1 = M(1)
+        m2 = M(2)
+        def f(x, y, n):
+            if x:
+                a = A()
+                a.m = m1
+                a.n = n
+            else:
+                a = B()
+                a.m = m2
+                a.n = n
+            a.x = 0
+            res = 0
+            while y > 0:
+                myjitdriver.jit_merge_point(y=y, n=n, res=res, a=a)
+                n1 = a.n
+                m = jit.promote(a.m)
+                res += m.x
+                a.x += 1
+                if y > n:
+                    res += 1
+                m = jit.promote(a.m)
+                res += m.x
+                res += n1 + a.n
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 32, 16])
+        assert res == f(6, 32, 16)
+        self.check_trace_count(3)
+        self.check_resops(guard_value=1)
+        self.check_resops(getfield_gc_i=4) # 3x a.x, 1x a.n
+        self.check_resops(getfield_gc_r=1) # in main loop
+
diff --git a/rpython/jit/metainterp/test/test_compile.py b/rpython/jit/metainterp/test/test_compile.py
--- a/rpython/jit/metainterp/test/test_compile.py
+++ b/rpython/jit/metainterp/test/test_compile.py
@@ -79,6 +79,7 @@
 def test_compile_loop():
     cpu = FakeCPU()
     staticdata = FakeMetaInterpStaticData()
+    staticdata.all_descrs = LLtypeMixin.cpu.setup_descrs()
     staticdata.cpu = cpu
     staticdata.jitlog = jl.JitLogger(cpu)
     staticdata.jitlog.trace_id = 1
diff --git a/rpython/jit/metainterp/test/test_greenfield.py b/rpython/jit/metainterp/test/test_greenfield.py
--- a/rpython/jit/metainterp/test/test_greenfield.py
+++ b/rpython/jit/metainterp/test/test_greenfield.py
@@ -49,7 +49,7 @@
         #
         res = self.meta_interp(g, [7])
         assert res == -22
-        self.check_trace_count(6)
+        self.check_trace_count(4)
         self.check_resops(guard_value=0)
 
     def test_green_field_3(self):
diff --git a/rpython/jit/metainterp/test/test_resume.py b/rpython/jit/metainterp/test/test_resume.py
--- a/rpython/jit/metainterp/test/test_resume.py
+++ b/rpython/jit/metainterp/test/test_resume.py
@@ -36,10 +36,12 @@
     rd_consts = []
     rd_virtuals = None
     rd_pendingfields = None
-    rd_count = 0
 
 
 class FakeOptimizer(object):
+    metainterp_sd = None
+    optheap = None
+
     def __init__(self, trace=None):
         self.trace = trace
 
@@ -251,18 +253,17 @@
     def get_current_position_info(self):
         class MyInfo:
             @staticmethod
-            def enumerate_vars(callback_i, callback_r, callback_f, _, index):
+            def enumerate_vars(callback_i, callback_r, callback_f, _):
                 count_i = count_r = count_f = 0
                 for ARG in self.ARGS:
                     if ARG == lltype.Signed:
-                        index = callback_i(index, count_i); count_i += 1
+                        callback_i(count_i); count_i += 1
                     elif ARG == llmemory.GCREF:
-                        index = callback_r(index, count_r); count_r += 1
+                        callback_r(count_r); count_r += 1
                     elif ARG == longlong.FLOATSTORAGE:
-                        index = callback_f(index, count_f); count_f += 1
+                        callback_f(count_f); count_f += 1
                     else:
                         assert 0
-                return index
         return MyInfo()
 
     def setarg_i(self, index, value):
@@ -289,7 +290,8 @@
     assert bh.written_f == expected_f
 
 
-Numbering = create_numbering
+def Numbering(l):
+    return create_numbering([len(l)] + l) # prefix index to the end of thing
 
 def tagconst(i):
     return tag(i + TAG_CONST_OFFSET, TAGCONST)
@@ -299,12 +301,11 @@
     c1, c2, c3 = [ConstInt(111), ConstInt(222), ConstInt(333)]
     storage = Storage()
     storage.rd_consts = [c1, c2, c3]
-    numb = Numbering([tag(0, TAGBOX), tagconst(0),
+    numb = Numbering([3, tag(0, TAGBOX), tagconst(0),
                        NULLREF, tag(0, TAGBOX), tag(1, TAGBOX)] +
-                       [tagconst(1), tagconst(2)] + 
+                       [tagconst(1), tagconst(2)] +
                        [tag(0, TAGBOX), tag(1, TAGBOX), tag(2, TAGBOX)])
     storage.rd_numb = numb
-    storage.rd_count = 3
     #
     cpu = MyCPU([42, gcref1, -66])
     metainterp = MyMetaInterp(cpu)
@@ -345,7 +346,7 @@
 def test_simple_read_tagged_ints():
     storage = Storage()
     storage.rd_consts = []
-    numb = Numbering([tag(100, TAGINT)])
+    numb = Numbering([1, tag(100, TAGINT)])
     storage.rd_numb = numb
     #
     cpu = MyCPU([])
@@ -362,10 +363,9 @@
             return s
     class FakeStorage(object):
         rd_virtuals = [FakeVinfo(), None]
-        rd_numb = []
+        rd_numb = Numbering([1])
         rd_consts = []
         rd_pendingfields = None
-        rd_count = 0
     class FakeMetainterp(object):
         _already_allocated_resume_virtuals = None
         cpu = None
@@ -773,12 +773,12 @@
     assert untag(tagged) == (44, TAGINT)
     tagged = memo.getconst(ConstInt(-3))
     assert untag(tagged) == (-3, TAGINT)
-    const = ConstInt(50000)
+    const = ConstInt(5000000)
     tagged = memo.getconst(const)
     index, tagbits = untag(tagged)
     assert tagbits == TAGCONST
     assert memo.consts[index - TAG_CONST_OFFSET] is const
-    tagged = memo.getconst(ConstInt(50000))
+    tagged = memo.getconst(ConstInt(5000000))
     index2, tagbits = untag(tagged)
     assert tagbits == TAGCONST
     assert index2 == index
@@ -858,7 +858,7 @@
     base = [0, 0, tag(0, TAGBOX), tag(1, TAGINT),
             tag(1, TAGBOX), tag(0, TAGBOX), tag(2, TAGINT)]
 
-    assert unpack_numbering(numb) == [0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
+    assert unpack_numbering(numb) == [17, 0, 0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
                                       tag(0, TAGBOX), tag(1, TAGINT)]
     t.append(0)
     snap2 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame(env2),
@@ -872,7 +872,7 @@
     assert numb_state2.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                      b3: tag(2, TAGBOX)}
     assert numb_state2.liveboxes is not numb_state.liveboxes
-    assert unpack_numbering(numb2) == [0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
+    assert unpack_numbering(numb2) == [17, 0, 0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX),
                                        tag(0, TAGBOX), tag(3, TAGINT)]
 
     t.append(0)
@@ -894,7 +894,7 @@
     assert numb_state3.num_virtuals == 0
     
     assert numb_state3.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX)}
-    assert unpack_numbering(numb3) == ([0, 2, tag(3, TAGINT), tag(4, TAGINT),
+    assert unpack_numbering(numb3) == ([17, 0, 0, 2, tag(3, TAGINT), tag(4, TAGINT),
                                        tag(0, TAGBOX), tag(3, TAGINT)] +
                                        base + [0, 2])
 
@@ -911,7 +911,7 @@
     
     assert numb_state4.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                      b4: tag(0, TAGVIRTUAL)}
-    assert unpack_numbering(numb4) == [0, 2, tag(3, TAGINT), tag(0, TAGVIRTUAL),
+    assert unpack_numbering(numb4) == [17, 0, 0, 2, tag(3, TAGINT), tag(0, TAGVIRTUAL),
                                        tag(0, TAGBOX), tag(3, TAGINT)] + base + [0, 2]
 
     t.append(0)
@@ -930,7 +930,7 @@
 
     assert numb_state5.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX),
                                      b4: tag(0, TAGVIRTUAL), b5: tag(1, TAGVIRTUAL)}
-    assert unpack_numbering(numb5) == [
+    assert unpack_numbering(numb5) == [22, 0,
         3, tag(0, TAGBOX), tag(0, TAGVIRTUAL), tag(1, TAGVIRTUAL),
         0] + base + [
         2, 1, tag(3, TAGINT), tag(0, TAGVIRTUAL), tag(0, TAGBOX), tag(3, TAGINT)
@@ -949,15 +949,17 @@
     numb_state = memo.number(FakeOptimizer(), 0, i)
     numb = numb_state.create_numbering()
     l = unpack_numbering(numb)
-    assert l[0] == 0
+    assert l[0] == len(l)
+    assert l[1] == 0
     assert l[1] == 0
     assert l[2] == 0
     assert l[3] == 0
+    assert l[4] == 0
     mapping = dict(zip(inpargs, i.inputargs))
     for i, item in enumerate(lst):
-        v, tag = untag(l[i + 4])
+        v, tag = untag(l[i + 6])
         if tag == TAGBOX:
-            assert l[i + 4] == numb_state.liveboxes[mapping[item]]
+            assert l[i + 6] == numb_state.liveboxes[mapping[item]]
         elif tag == TAGCONST:
             assert memo.consts[v].getint() == item.getint()
         elif tag == TAGINT:
@@ -1069,15 +1071,15 @@
     cpu = MyCPU([])
     reader = ResumeDataDirectReader(MyMetaInterp(cpu), storage, "deadframe")
     reader.consume_vref_and_vable(None, None, None)
-    reader.cur_index += 2 # framestack
+    reader.resumecodereader.jump(2) # framestack
     _next_section(reader, sys.maxint, 1, sys.maxint, 2**16)
-    reader.cur_index += 2 # framestack
+    reader.resumecodereader.jump(2) # framestack
     _next_section(reader, 2, 3)
-    reader.cur_index += 2 # framestack
+    reader.resumecodereader.jump(2) # framestack
     _next_section(reader, sys.maxint, 2**16, -65)
 
 def test_virtual_adder_memo_const_sharing():
-    b1s, b2s, b3s = [ConstInt(sys.maxint), ConstInt(2**16), ConstInt(-65)]
+    b1s, b2s, b3s = [ConstInt(sys.maxint), ConstInt(2**23), ConstInt(-65)]
     storage, t = make_storage(b1s, b2s, b3s)
     metainterp_sd = FakeMetaInterpStaticData()
     memo = ResumeDataLoopMemo(metainterp_sd)
@@ -1087,7 +1089,7 @@
     assert len(memo.consts) == 2
     assert storage.rd_consts is memo.consts
 
-    b1s, b2s, b3s = [ConstInt(sys.maxint), ConstInt(2**17), ConstInt(-65)]
+    b1s, b2s, b3s = [ConstInt(sys.maxint), ConstInt(2**24), ConstInt(-65)]
     storage2, t = make_storage(b1s, b2s, b3s)
     i = t.get_iter()
     modifier2 = ResumeDataVirtualAdder(FakeOptimizer(i), storage2, storage2,
@@ -1112,9 +1114,10 @@
                 return True
         class MyInfo:
             @staticmethod
-            def enumerate_vars(callback_i, callback_r, callback_f, _, index):
-                while index < len(self.numb.code):
-                    tagged, _ = resumecode.numb_next_item(self.numb, index)
+            def enumerate_vars(callback_i, callback_r, callback_f, _):
+                index = 0
+                while not self.done_reading():
+                    tagged = self.resumecodereader.peek()
                     _, tag = untag(tagged)
                     if tag == TAGVIRTUAL:
                         kind = REF
@@ -1122,20 +1125,21 @@
                         kind = Whatever()
                     box = self.decode_box(tagged, kind)
                     if box.type == INT:
-                        index = callback_i(index, index)
+                        callback_i(index)
                     elif box.type == REF:
-                        index = callback_r(index, index)
+                        callback_r(index)
                     elif box.type == FLOAT:
-                        index = callback_f(index, index)
+                        callback_f(index)
                     else:
                         assert 0
+                    index += 1
 
-        size, self.cur_index = resumecode.numb_next_item(self.numb, 0)
+        size = self.resumecodereader.next_item()
         assert size == 0
-        size, self.cur_index = resumecode.numb_next_item(self.numb, self.cur_index)
+        size = self.resumecodereader.next_item()
         assert size == 0
-        pc, self.cur_index = resumecode.numb_next_item(self.numb, self.cur_index)
-        jitcode_pos, self.cur_index = resumecode.numb_next_item(self.numb, self.cur_index)
+        pc = self.resumecodereader.next_item()
+        jitcode_pos = self.resumecodereader.next_item()
 
         self._prepare_next_section(MyInfo())
         return self.lst
@@ -1228,7 +1232,7 @@
     liveboxes = []
     modifier._number_virtuals(liveboxes, FakeOptimizer(), 0)
     storage.rd_consts = memo.consts[:]
-    storage.rd_numb = None
+    storage.rd_numb = Numbering([0])
     # resume
     b3t, b5t = [IntFrontendOp(0), RefFrontendOp(0)]
     b5t.setref_base(demo55o)
@@ -1299,7 +1303,7 @@
     modifier._number_virtuals(liveboxes, FakeOptimizer(), 0)
     dump_storage(storage, liveboxes)
     storage.rd_consts = memo.consts[:]
-    storage.rd_numb = None
+    storage.rd_numb = Numbering([0])
     # resume
     b1t, b3t, b4t = [IntFrontendOp(0), IntFrontendOp(0), IntFrontendOp(0)]
     b1t.setint(11)
@@ -1352,7 +1356,7 @@
     modifier._number_virtuals(liveboxes, FakeOptimizer(), 0)
     dump_storage(storage, liveboxes)
     storage.rd_consts = memo.consts[:]
-    storage.rd_numb = None
+    storage.rd_numb = Numbering([0])
     b4t = RefFrontendOp(0)
     newboxes = _resume_remap(liveboxes, [#b2s -- virtual
                                          b4s], b4t)
@@ -1398,7 +1402,7 @@
     modifier._add_pending_fields(FakeOptimizer(), [
         ResOperation(rop.SETFIELD_GC, [b2s, b4s], descr=LLtypeMixin.nextdescr)])
     storage.rd_consts = memo.consts[:]
-    storage.rd_numb = None
+    storage.rd_numb = Numbering([0])
     # resume
     demo55.next = lltype.nullptr(LLtypeMixin.NODE)
     b2t = RefFrontendOp(0)
diff --git a/rpython/jit/metainterp/test/test_resumecode.py b/rpython/jit/metainterp/test/test_resumecode.py
--- a/rpython/jit/metainterp/test/test_resumecode.py
+++ b/rpython/jit/metainterp/test/test_resumecode.py
@@ -1,29 +1,62 @@
-
-from rpython.jit.metainterp.resumecode import NUMBERING, NULL_NUMBER
 from rpython.jit.metainterp.resumecode import create_numbering,\
-    unpack_numbering
+    unpack_numbering, Reader, Writer
 from rpython.rtyper.lltypesystem import lltype
 
-from hypothesis import strategies, given
+from hypothesis import strategies, given, example
 
+examples = [
+    [1, 2, 3, 4, 257, 10000, 13, 15],
+    [1, 2, 3, 4],
+    range(1, 10, 2),
+    [13000, 12000, 10000, 256, 255, 254, 257, -3, -1000]
+]
 
-def test_pack_unpack():
-    examples = [
-        [1, 2, 3, 4, 257, 10000, 13, 15],
-        [1, 2, 3, 4],
-        range(1, 10, 2),
-        [13000, 12000, 10000, 256, 255, 254, 257, -3, -1000]
-    ]
-    for l in examples:
-        n = create_numbering(l)
-        assert unpack_numbering(n) == l
+def hypothesis_and_examples(func):
+    func = given(strategies.lists(strategies.integers(-2**15, 2**15-1)))(func)
+    for ex in examples:
+        func = example(ex)(func)
+    return func
 
- at given(strategies.lists(strategies.integers(-2**15, 2**15-1)))
+ at hypothesis_and_examples
 def test_roundtrip(l):
     n = create_numbering(l)
     assert unpack_numbering(n) == l
 
- at given(strategies.lists(strategies.integers(-2**15, 2**15-1)))
+ at hypothesis_and_examples
 def test_compressing(l):
     n = create_numbering(l)
     assert len(n.code) <= len(l) * 3
+
+ at hypothesis_and_examples
+def test_reader(l):
+    n = create_numbering(l)
+    r = Reader(n)
+    for i, elt in enumerate(l):
+        assert r.items_read == i
+        item = r.next_item()
+        assert elt == item
+
+ at hypothesis_and_examples
+def test_writer(l):
+    for size in [len(l), 0]:
+        w = Writer(len(l))
+        for num in l:
+            w.append_int(num)
+        n = w.create_numbering()
+        assert unpack_numbering(n) == l
+
+ at hypothesis_and_examples
+def test_patch(l):
+    for middle in range(len(l)):
+        l1 = l[:middle]
+        l2 = l[middle:]
+        w = Writer(len(l))
+        w.append_int(0)
+        for num in l1:
+            w.append_int(num)
+        w.patch_current_size(0)
+        for num in l2:
+            w.append_int(num)
+        n = w.create_numbering()
+        assert unpack_numbering(n)[1:] == l
+        assert unpack_numbering(n)[0] == middle + 1
diff --git a/rpython/jit/metainterp/virtualizable.py b/rpython/jit/metainterp/virtualizable.py
--- a/rpython/jit/metainterp/virtualizable.py
+++ b/rpython/jit/metainterp/virtualizable.py
@@ -2,7 +2,6 @@
 from rpython.jit.metainterp import history
 from rpython.jit.metainterp.typesystem import deref, fieldType, arrayItem
 from rpython.jit.metainterp.warmstate import wrap, unwrap
-from rpython.jit.metainterp.resumecode import numb_next_item
 from rpython.rlib.unroll import unrolling_iterable
 from rpython.rtyper import rvirtualizable
 from rpython.rtyper.lltypesystem import lltype, llmemory
@@ -127,24 +126,21 @@
                 size += 1
             return size
 
-        def write_from_resume_data_partial(virtualizable, reader, index, numb):
+        def write_from_resume_data_partial(virtualizable, reader):
             virtualizable = cast_gcref_to_vtype(virtualizable)
             # Load values from the reader (see resume.py) described by
             # the list of numbers 'nums', and write them in their proper
             # place in the 'virtualizable'.
             for FIELDTYPE, fieldname in unroll_static_fields:
-                item, index = numb_next_item(numb, index)
-                x = reader.load_value_of_type(FIELDTYPE, item)
+                x = reader.load_next_value_of_type(FIELDTYPE)
                 setattr(virtualizable, fieldname, x)
             for ARRAYITEMTYPE, fieldname in unroll_array_fields:
                 lst = getattr(virtualizable, fieldname)
                 for j in range(getlength(lst)):
-                    item, index = numb_next_item(numb, index)                    
-                    x = reader.load_value_of_type(ARRAYITEMTYPE, item)
+                    x = reader.load_next_value_of_type(ARRAYITEMTYPE)
                     setarrayitem(lst, j, x)
-            return index
 
-        def load_list_of_boxes(virtualizable, reader, vable_box, numb, index):
+        def load_list_of_boxes(virtualizable, reader, vable_box):
             virtualizable = cast_gcref_to_vtype(virtualizable)
             # Uses 'virtualizable' only to know the length of the arrays;
             # does not write anything into it.  The returned list is in
@@ -152,17 +148,15 @@
             # the virtualizable itself.
             boxes = []
             for FIELDTYPE, fieldname in unroll_static_fields:
-                item, index = numb_next_item(numb, index)
-                box = reader.decode_box_of_type(FIELDTYPE, item)
+                box = reader.next_box_of_type(FIELDTYPE)
                 boxes.append(box)
             for ARRAYITEMTYPE, fieldname in unroll_array_fields:
                 lst = getattr(virtualizable, fieldname)
                 for j in range(getlength(lst)):
-                    item, index = numb_next_item(numb, index)                    
-                    box = reader.decode_box_of_type(ARRAYITEMTYPE, item)
+                    box = reader.next_box_of_type(ARRAYITEMTYPE)
                     boxes.append(box)
             boxes.append(vable_box)
-            return boxes, index
+            return boxes
 
         def check_boxes(virtualizable, boxes):
             virtualizable = cast_gcref_to_vtype(virtualizable)


More information about the pypy-commit mailing list