[pypy-commit] pypy default: merge heads

rlamy pypy.commits at gmail.com
Sun Feb 26 08:58:33 EST 2017


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: 
Changeset: r90364:e28dd1841ff7
Date: 2017-02-26 14:57 +0100
http://bitbucket.org/pypy/pypy/changeset/e28dd1841ff7/

Log:	merge heads

diff --git a/pypy/doc/faq.rst b/pypy/doc/faq.rst
--- a/pypy/doc/faq.rst
+++ b/pypy/doc/faq.rst
@@ -156,19 +156,30 @@
 Does PyPy have a GIL?  Why?
 -------------------------------------------------
 
-Yes, PyPy has a GIL.  Removing the GIL is very hard.  The problems are
-essentially the same as with CPython (including the fact that our
-garbage collectors are not thread-safe so far).  Fixing it is possible,
-as shown by Jython and IronPython, but difficult.  It would require
-adapting the whole source code of PyPy, including subtle decisions about
-whether some effects are ok or not for the user (i.e. the Python
-programmer).
+Yes, PyPy has a GIL.  Removing the GIL is very hard.  On top of CPython,
+you have two problems:  (1) GC, in this case reference counting; (2) the
+whole Python language.
 
-Instead, since 2012, there is work going on on a still very experimental
-:doc:`Software Transactional Memory <stm>` (STM) version of PyPy.  This should give
-an alternative PyPy which works without a GIL, while at the same time
-continuing to give the Python programmer the complete illusion of having
-one.
+For PyPy, the hard issue is (2): by that I mean issues like what occurs
+if a mutable object is changed from one thread and read from another
+concurrently.  This is a problem for *any* mutable type: it needs
+careful review and fixes (fine-grained locks, mostly) through the
+*whole* Python interpreter.  It is a major effort, although not
+completely impossible, as Jython/IronPython showed.  This includes
+subtle decisions about whether some effects are ok or not for the user
+(i.e. the Python programmer).
+
+CPython has additionally the problem (1) of reference counting.  With
+PyPy, this sub-problem is simpler: we need to make our GC
+multithread-aware.  This is easier to do efficiently in PyPy than in
+CPython.  It doesn't solve the issue (2), though.
+
+Note that since 2012 there is work going on on a still very experimental
+:doc:`Software Transactional Memory <stm>` (STM) version of PyPy.  This
+should give an alternative PyPy which works without a GIL, while at the
+same time continuing to give the Python programmer the complete illusion
+of having one.  This work is currently a bit stalled because of its own
+technical difficulties.
 
 
 Is PyPy more clever than CPython about Tail Calls?
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/info.py b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -70,7 +70,7 @@
     def same_info(self, other):
         return self is other
 
-    def getstrlen(self, op, string_optimizer, mode, create_ops=True):
+    def getstrlen(self, op, string_optimizer, mode):
         return None
 
     def getstrhash(self, op, mode):
@@ -777,21 +777,20 @@
             # XXX we can do better if we know it's an array
             return IntLowerBound(0)
         else:
-            return ConstIntBound(self.getstrlen(None, None, mode).getint())
-    
-    def getstrlen(self, op, string_optimizer, mode, create_ops=True):
+            return ConstIntBound(self.getstrlen1(mode))
+
+    def getstrlen(self, op, string_optimizer, mode):
+        return ConstInt(self.getstrlen1(mode))
+
+    def getstrlen1(self, mode):
         from rpython.jit.metainterp.optimizeopt import vstring
         
         if mode is vstring.mode_string:
             s = self._unpack_str(vstring.mode_string)
-            if s is None:
-                return None
-            return ConstInt(len(s))
+            return len(s)
         else:
             s = self._unpack_str(vstring.mode_unicode)            
-            if s is None:
-                return None
-            return ConstInt(len(s))
+            return len(s)
 
     def getstrhash(self, op, mode):
         from rpython.jit.metainterp.optimizeopt import vstring
@@ -812,7 +811,7 @@
         from rpython.jit.metainterp.optimizeopt import vstring
         from rpython.jit.metainterp.optimizeopt.optimizer import CONST_0
 
-        lgt = self.getstrlen(op, string_optimizer, mode, False)
+        lgt = self.getstrlen(op, string_optimizer, mode)
         return vstring.copy_str_content(string_optimizer, self._const,
                                         targetbox, CONST_0, offsetbox,
                                         lgt, mode)
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