Author: fijal
Branch: 
Changeset: r83364:913dd4cf97ff
Date: 2016-03-25 20:54 +0200
http://bitbucket.org/pypy/pypy/changeset/913dd4cf97ff/

Log:    (fijal, arigo) Merge jit-leaner-frontend

        This branch separates resoperations used in optimizer from the ones
        used in the frontend, which now uses a more compact way to store
        traces. Additionally the heapcache has been reworked to the new
        model. The net effects are ~20% improvements in the speed of
        tracing. There is potential for more work which would avoid
        allocating ResOperations that are not emitted completely

diff too long, truncating to 2000 out of 8331 lines

diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -46,7 +46,6 @@
 except detect_cpu.ProcessorAutodetectError:
     pass
 
-
 translation_modules = default_modules.copy()
 translation_modules.update([
     "fcntl", "time", "select", "signal", "_rawffi", "zlib", "struct", "_md5",
diff --git a/pypy/tool/gdb_pypy.py b/pypy/tool/gdb_pypy.py
--- a/pypy/tool/gdb_pypy.py
+++ b/pypy/tool/gdb_pypy.py
@@ -288,9 +288,11 @@
             RPyListPrinter.recursive = True
             try:
                 itemlist = []
-                for i in range(length):
+                for i in range(min(length, MAX_DISPLAY_LENGTH)):
                     item = items[i]
                     itemlist.append(str(item))    # may recurse here
+                if length > MAX_DISPLAY_LENGTH:
+                    itemlist.append("...")
                 str_items = ', '.join(itemlist)
             finally:
                 RPyListPrinter.recursive = False
diff --git a/rpython/config/translationoption.py 
b/rpython/config/translationoption.py
--- a/rpython/config/translationoption.py
+++ b/rpython/config/translationoption.py
@@ -126,6 +126,9 @@
     ChoiceOption("jit_profiler", "integrate profiler support into the JIT",
                  ["off", "oprofile"],
                  default="off"),
+    ChoiceOption("jit_opencoder_model", "the model limits the maximal length"
+                 " of traces. Use big if you want to go bigger than "
+                 "the default", ["big", "normal"], default="normal"),
     BoolOption("check_str_without_nul",
                "Forbid NUL chars in strings in some external function calls",
                default=False, cmdline=None),
diff --git a/rpython/jit/backend/llgraph/runner.py 
b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -455,7 +455,7 @@
                 if box is not frame.current_op:
                     value = frame.env[box]
                 else:
-                    value = box.getvalue()    # 0 or 0.0 or NULL
+                    value = 0 # box.getvalue()    # 0 or 0.0 or NULL
             else:
                 value = None
             values.append(value)
@@ -472,6 +472,13 @@
 
     # ------------------------------------------------------------
 
+    def setup_descrs(self):
+        all_descrs = []
+        for k, v in self.descrs.iteritems():
+            v.descr_index = len(all_descrs)
+            all_descrs.append(v)
+        return all_descrs
+
     def calldescrof(self, FUNC, ARGS, RESULT, effect_info):
         key = ('call', getkind(RESULT),
                tuple([getkind(A) for A in ARGS]),
diff --git a/rpython/jit/backend/llsupport/assembler.py 
b/rpython/jit/backend/llsupport/assembler.py
--- a/rpython/jit/backend/llsupport/assembler.py
+++ b/rpython/jit/backend/llsupport/assembler.py
@@ -331,7 +331,7 @@
         counter = self._register_counter(tp, number, token)
         c_adr = ConstInt(rffi.cast(lltype.Signed, counter))
         operations.append(
-            ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr], None))
+            ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr]))
 
     def _register_counter(self, tp, number, token):
         # YYY very minor leak -- we need the counters to stay alive
diff --git a/rpython/jit/backend/llsupport/descr.py 
b/rpython/jit/backend/llsupport/descr.py
--- a/rpython/jit/backend/llsupport/descr.py
+++ b/rpython/jit/backend/llsupport/descr.py
@@ -21,6 +21,30 @@
         self._cache_call = {}
         self._cache_interiorfield = {}
 
+    def setup_descrs(self):
+        all_descrs = []
+        for k, v in self._cache_size.iteritems():
+            v.descr_index = len(all_descrs)
+            all_descrs.append(v)
+        for k, v in self._cache_field.iteritems():
+            for k1, v1 in v.iteritems():
+                v1.descr_index = len(all_descrs)
+                all_descrs.append(v1)
+        for k, v in self._cache_array.iteritems():
+            v.descr_index = len(all_descrs)
+            all_descrs.append(v)
+        for k, v in self._cache_arraylen.iteritems():
+            v.descr_index = len(all_descrs)
+            all_descrs.append(v)
+        for k, v in self._cache_call.iteritems():
+            v.descr_index = len(all_descrs)
+            all_descrs.append(v)
+        for k, v in self._cache_interiorfield.iteritems():
+            v.descr_index = len(all_descrs)
+            all_descrs.append(v)
+        assert len(all_descrs) < 2**15
+        return all_descrs
+
     def init_size_descr(self, STRUCT, sizedescr):
         pass
 
diff --git a/rpython/jit/backend/llsupport/llmodel.py 
b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -316,6 +316,9 @@
             return ll_frame
         return execute_token
 
+    def setup_descrs(self):
+        return self.gc_ll_descr.setup_descrs()
+
     # ------------------- helpers and descriptions --------------------
 
     @staticmethod
diff --git a/rpython/jit/backend/llsupport/regalloc.py 
b/rpython/jit/backend/llsupport/regalloc.py
--- a/rpython/jit/backend/llsupport/regalloc.py
+++ b/rpython/jit/backend/llsupport/regalloc.py
@@ -683,7 +683,7 @@
     for i in range(len(operations)-1, -1, -1):
         op = operations[i]
         if op.type != 'v':
-            if op not in last_used and op.has_no_side_effect():
+            if op not in last_used and rop.has_no_side_effect(op.opnum):
                 continue
         opnum = op.getopnum()
         for j in range(op.numargs()):
@@ -695,7 +695,7 @@
             if opnum != rop.JUMP and opnum != rop.LABEL:
                 if arg not in last_real_usage:
                     last_real_usage[arg] = i
-        if op.is_guard():
+        if rop.is_guard(op.opnum):
             for arg in op.getfailargs():
                 if arg is None: # hole
                     continue
@@ -732,14 +732,7 @@
     return longevity, last_real_usage
 
 def is_comparison_or_ovf_op(opnum):
-    from rpython.jit.metainterp.resoperation import opclasses
-    cls = opclasses[opnum]
-    # hack hack: in theory they are instance method, but they don't use
-    # any instance field, we can use a fake object
-    class Fake(cls):
-        pass
-    op = Fake()
-    return op.is_comparison() or op.is_ovf()
+    return rop.is_comparison(opnum) or rop.is_ovf(opnum)
 
 def valid_addressing_size(size):
     return size == 1 or size == 2 or size == 4 or size == 8
diff --git a/rpython/jit/backend/llsupport/rewrite.py 
b/rpython/jit/backend/llsupport/rewrite.py
--- a/rpython/jit/backend/llsupport/rewrite.py
+++ b/rpython/jit/backend/llsupport/rewrite.py
@@ -103,7 +103,7 @@
                     orig_op.set_forwarded(op)
                     replaced = True
                 op.setarg(i, arg)
-        if op.is_guard():
+        if rop.is_guard(op.opnum):
             if not replaced:
                 op = op.copy_and_change(op.getopnum())
                 orig_op.set_forwarded(op)
@@ -212,7 +212,7 @@
         #                
self._emit_mul_if_factor_offset_not_supported(v_length, scale, 0)
         #        op.setarg(1, ConstInt(scale))
         #        op.setarg(2, v_length)
-        if op.is_getarrayitem() or \
+        if rop.is_getarrayitem(opnum) or \
            opnum in (rop.GETARRAYITEM_RAW_I,
                      rop.GETARRAYITEM_RAW_F):
             self.handle_getarrayitem(op)
@@ -324,13 +324,13 @@
             if self.transform_to_gc_load(op):
                 continue
             # ---------- turn NEWxxx into CALL_MALLOC_xxx ----------
-            if op.is_malloc():
+            if rop.is_malloc(op.opnum):
                 self.handle_malloc_operation(op)
                 continue
-            if (op.is_guard() or
+            if (rop.is_guard(op.opnum) or
                     self.could_merge_with_next_guard(op, i, operations)):
                 self.emit_pending_zeros()
-            elif op.can_malloc():
+            elif rop.can_malloc(op.opnum):
                 self.emitting_an_operation_that_can_collect()
             elif op.getopnum() == rop.LABEL:
                 self.emitting_an_operation_that_can_collect()
@@ -370,8 +370,8 @@
         # return True in cases where the operation and the following guard
         # should likely remain together.  Simplified version of
         # can_merge_with_next_guard() in llsupport/regalloc.py.
-        if not op.is_comparison():
-            return op.is_ovf()    # int_xxx_ovf() / guard_no_overflow()
+        if not rop.is_comparison(op.opnum):
+            return rop.is_ovf(op.opnum)    # int_xxx_ovf() / 
guard_no_overflow()
         if i + 1 >= len(operations):
             return False
         next_op = operations[i + 1]
@@ -400,7 +400,6 @@
         # it's hard to test all cases).  Rewrite it away.
         value = int(opnum == rop.GUARD_FALSE)
         op1 = ResOperation(rop.SAME_AS_I, [ConstInt(value)])
-        op1.setint(value)
         self.emit_op(op1)
         lst = op.getfailargs()[:]
         lst[i] = op1
@@ -633,8 +632,7 @@
             args = [frame, arglist[jd.index_of_virtualizable]]
         else:
             args = [frame]
-        call_asm = ResOperation(op.getopnum(), args,
-                                op.getdescr())
+        call_asm = ResOperation(op.getopnum(), args, descr=op.getdescr())
         self.replace_op_with(self.get_box_replacement(op), call_asm)
         self.emit_op(call_asm)
 
@@ -708,7 +706,7 @@
     def _gen_call_malloc_gc(self, args, v_result, descr):
         """Generate a CALL_MALLOC_GC with the given args."""
         self.emitting_an_operation_that_can_collect()
-        op = ResOperation(rop.CALL_MALLOC_GC, args, descr)
+        op = ResOperation(rop.CALL_MALLOC_GC, args, descr=descr)
         self.replace_op_with(v_result, op)
         self.emit_op(op)
         # In general, don't add v_result to write_barrier_applied:
diff --git a/rpython/jit/backend/test/test_ll_random.py 
b/rpython/jit/backend/test/test_ll_random.py
--- a/rpython/jit/backend/test/test_ll_random.py
+++ b/rpython/jit/backend/test/test_ll_random.py
@@ -2,6 +2,7 @@
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rstr
 from rpython.rtyper import rclass
 from rpython.jit.backend.test import test_random
+from rpython.jit.backend.test.test_random import getint, getref_base, getref
 from rpython.jit.metainterp.resoperation import ResOperation, rop, optypes
 from rpython.jit.metainterp.history import ConstInt, ConstPtr, getkind
 from rpython.jit.codewriter import heaptracker
@@ -169,7 +170,7 @@
         if length == 0:
             raise test_random.CannotProduceOperation
         v_index = r.choice(self.intvars)
-        if not (0 <= v_index.getint() < length):
+        if not (0 <= getint(v_index) < length):
             v_index = ConstInt(r.random_integer() % length)
         return v_index
 
@@ -311,7 +312,7 @@
     def field_descr(self, builder, r):
         v, A = builder.get_structptr_var(r, type=lltype.Array,
                                          array_of_structs=True)
-        array = v.getref(lltype.Ptr(A))
+        array = getref(lltype.Ptr(A), v)
         v_index = builder.get_index(len(array), r)
         choice = []
         for name in A.OF._names:
@@ -344,7 +345,7 @@
                 w = ConstInt(r.random_integer())
             else:
                 w = r.choice(builder.intvars)
-            value = w.getint()
+            value = getint(w)
             if rffi.cast(lltype.Signed, rffi.cast(TYPE, value)) == value:
                 break
         builder.do(self.opnum, [v, w], descr)
@@ -357,7 +358,7 @@
                 w = ConstInt(r.random_integer())
             else:
                 w = r.choice(builder.intvars)
-            value = w.getint()
+            value = getint(w)
             if rffi.cast(lltype.Signed, rffi.cast(TYPE, value)) == value:
                 break
         builder.do(self.opnum, [v, v_index, w], descr)
@@ -389,7 +390,7 @@
 class GetArrayItemOperation(ArrayOperation):
     def field_descr(self, builder, r):
         v, A = builder.get_arrayptr_var(r)
-        array = v.getref(lltype.Ptr(A))
+        array = getref(lltype.Ptr(A), v)
         v_index = builder.get_index(len(array), r)
         descr = self.array_descr(builder, A)
         return v, A, v_index, descr
@@ -411,7 +412,7 @@
                 w = ConstInt(r.random_integer())
             else:
                 w = r.choice(builder.intvars)
-            value = w.getint()
+            value = getint(w)
             if rffi.cast(lltype.Signed, rffi.cast(A.OF, value)) == value:
                 break
         builder.do(self.opnum, [v, v_index, w], descr)
@@ -455,7 +456,7 @@
         v_ptr = builder.do(self.opnum, [v_length])
         getattr(builder, self.builder_cache).append(v_ptr)
         # Initialize the string. Is there a better way to do this?
-        for i in range(v_length.getint()):
+        for i in range(getint(v_length)):
             v_index = ConstInt(i)
             v_char = ConstInt(r.random_integer() % self.max)
             builder.do(self.set_char, [v_ptr, v_index, v_char])
@@ -471,9 +472,9 @@
         current = getattr(builder, self.builder_cache)
         if current and r.random() < .8:
             v_string = r.choice(current)
-            string = v_string.getref(self.ptr)
+            string = getref(self.ptr, v_string)
         else:
-            string = self.alloc(builder.get_index(500, r).getint())
+            string = self.alloc(getint(builder.get_index(500, r)))
             v_string = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, string))
             current.append(v_string)
         for i in range(len(string.chars)):
@@ -484,7 +485,7 @@
 class AbstractGetItemOperation(AbstractStringOperation):
     def produce_into(self, builder, r):
         v_string = self.get_string(builder, r)
-        v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r)
+        v_index = builder.get_index(len(getref(self.ptr, v_string).chars), r)
         builder.do(self.opnum, [v_string, v_index])
 
 class AbstractSetItemOperation(AbstractStringOperation):
@@ -492,7 +493,7 @@
         v_string = self.get_string(builder, r)
         if isinstance(v_string, ConstPtr):
             raise test_random.CannotProduceOperation  # setitem(Const, ...)
-        v_index = builder.get_index(len(v_string.getref(self.ptr).chars), r)
+        v_index = builder.get_index(len(getref(self.ptr, v_string).chars), r)
         v_target = ConstInt(r.random_integer() % self.max)
         builder.do(self.opnum, [v_string, v_index, v_target])
 
@@ -505,15 +506,15 @@
     def produce_into(self, builder, r):
         v_srcstring = self.get_string(builder, r)
         v_dststring = self.get_string(builder, r)
-        src = v_srcstring.getref(self.ptr)
-        dst = v_dststring.getref(self.ptr)
+        src = getref(self.ptr, v_srcstring)
+        dst = getref(self.ptr, v_dststring)
         if src == dst:                                # because it's not a
             raise test_random.CannotProduceOperation  # memmove(), but memcpy()
         srclen = len(src.chars)
         dstlen = len(dst.chars)
         v_length = builder.get_index(min(srclen, dstlen), r)
-        v_srcstart = builder.get_index(srclen - v_length.getint() + 1, r)
-        v_dststart = builder.get_index(dstlen - v_length.getint() + 1, r)
+        v_srcstart = builder.get_index(srclen - getint(v_length) + 1, r)
+        v_dststart = builder.get_index(dstlen - getint(v_length) + 1, r)
         builder.do(self.opnum, [v_srcstring, v_dststring,
                                 v_srcstart, v_dststart, v_length])
 
@@ -585,7 +586,7 @@
         """ % funcargs).compile()
         vtableptr = v._hints['vtable']._as_ptr()
         d = {
-            'ptr': S.getref_base(),
+            'ptr': getref_base(S),
             'vtable' : vtableptr,
             'LLException' : LLException,
             }
diff --git a/rpython/jit/backend/test/test_random.py 
b/rpython/jit/backend/test/test_random.py
--- a/rpython/jit/backend/test/test_random.py
+++ b/rpython/jit/backend/test/test_random.py
@@ -11,11 +11,9 @@
 from rpython.jit.metainterp.executor import _execute_arglist, wrap_constant
 from rpython.jit.metainterp.resoperation import opname
 from rpython.jit.codewriter import longlong
-from rpython.rtyper.lltypesystem import lltype, rstr
+from rpython.rtyper.lltypesystem import lltype, llmemory, rstr
 from rpython.rtyper import rclass
 
-class PleaseRewriteMe(Exception):
-    pass
 
 class DummyLoop(object):
     def __init__(self, subops):
@@ -27,6 +25,41 @@
     def execute_raised(self, exc, constant=False):
         self._got_exc = exc
 
+
+def getint(v):
+    if isinstance(v, (ConstInt, InputArgInt)):
+        return v.getint()
+    else:
+        return v._example_int
+
+def getfloatstorage(v):
+    if isinstance(v, (ConstFloat, InputArgFloat)):
+        return v.getfloatstorage()
+    else:
+        return v._example_float
+
+def getfloat(v):
+    return longlong.getrealfloat(getfloatstorage(v))
+
+def getref_base(v):
+    if isinstance(v, (ConstPtr, InputArgRef)):
+        return v.getref_base()
+    else:
+        return v._example_ref
+
+def getref(PTR, v):
+    return lltype.cast_opaque_ptr(PTR, getref_base(v))
+
+def constbox(v):
+    if v.type == INT:
+        return ConstInt(getint(v))
+    if v.type == FLOAT:
+        return ConstFloat(getfloatstorage(v))
+    if v.type == REF:
+        return ConstPtr(getref_base(v))
+    assert 0, v.type
+
+
 class OperationBuilder(object):
     def __init__(self, cpu, loop, vars):
         self.cpu = cpu
@@ -57,11 +90,21 @@
     def do(self, opnum, argboxes, descr=None):
         self.fakemetainterp._got_exc = None
         op = ResOperation(opnum, argboxes, descr)
+        argboxes = map(constbox, argboxes)
         result = _execute_arglist(self.cpu, self.fakemetainterp,
                                   opnum, argboxes, descr)
         if result is not None:
-            c_result = wrap_constant(result)
-            op.copy_value_from(c_result)
+            if lltype.typeOf(result) == lltype.Signed:
+                op._example_int = result
+            elif isinstance(result, bool):
+                op._example_int = int(result)
+            elif lltype.typeOf(result) == longlong.FLOATSTORAGE:
+                op._example_float = result
+            elif isinstance(result, float):
+                op._example_float = longlong.getfloatstorage(result)
+            else:
+                assert lltype.typeOf(result) == llmemory.GCREF
+                op._example_ref = result
         self.loop.operations.append(op)
         return op
 
@@ -101,7 +144,7 @@
             if v in names:
                 args.append(names[v])
             elif isinstance(v, ConstPtr):
-                assert not v.getref_base() # otherwise should be in the names
+                assert not getref_base(v) # otherwise should be in the names
                 args.append('ConstPtr(lltype.nullptr(llmemory.GCREF.TO))')
             elif isinstance(v, ConstFloat):
                 args.append('ConstFloat(longlong.getfloatstorage(%r))'
@@ -198,10 +241,10 @@
         #
         def writevar(v, nameprefix, init=''):
             if nameprefix == 'const_ptr':
-                if not v.getref_base():
+                if not getref_base(v):
                     return 'lltype.nullptr(llmemory.GCREF.TO)'
-                TYPE = v.getref_base()._obj.ORIGTYPE
-                cont = lltype.cast_opaque_ptr(TYPE, v.getref_base())
+                TYPE = getref_base(v)._obj.ORIGTYPE
+                cont = lltype.cast_opaque_ptr(TYPE, getref_base(v))
                 if TYPE.TO._is_varsize():
                     if isinstance(TYPE.TO, lltype.GcStruct):
                         lgt = len(cont.chars)
@@ -252,9 +295,9 @@
             for i, v in enumerate(self.loop.inputargs):
                 assert not isinstance(v, Const)
                 if v.type == FLOAT:
-                    vals.append("longlong.getfloatstorage(%r)" % v.getfloat())
+                    vals.append("longlong.getfloatstorage(%r)" % getfloat(v))
                 else:
-                    vals.append("%r" % v.getint())
+                    vals.append("%r" % getint(v))
             print >>s, '    loop_args = [%s]' % ", ".join(vals)
         print >>s, '    frame = cpu.execute_token(looptoken, *loop_args)'
         if self.should_fail_by is None:
@@ -264,10 +307,10 @@
         for i, v in enumerate(fail_args):
             if v.type == FLOAT:
                 print >>s, ('    assert longlong.getrealfloat('
-                    'cpu.get_float_value(frame, %d)) == %r' % (i, 
v.getfloatstorage()))
+                    'cpu.get_float_value(frame, %d)) == %r' % (i, 
getfloatstorage(v)))
             else:
                 print >>s, ('    assert cpu.get_int_value(frame, %d) == %d'
-                            % (i, v.getint()))
+                            % (i, getint(v)))
         self.names = names
         s.flush()
 
@@ -295,7 +338,7 @@
                 builder.intvars.append(v_result)
                 boolres = self.boolres
                 if boolres == 'sometimes':
-                    boolres = v_result.getint() in [0, 1]
+                    boolres = getint(v_result) in [0, 1]
                 if boolres:
                     builder.boolvars.append(v_result)
             elif v_result.type == FLOAT:
@@ -346,10 +389,10 @@
             v_second = ConstInt((value & self.and_mask) | self.or_mask)
         else:
             v = r.choice(builder.intvars)
-            v_value = v.getint()
+            v_value = getint(v)
             if (v_value & self.and_mask) != v_value:
                 v = builder.do(rop.INT_AND, [v, ConstInt(self.and_mask)])
-            v_value = v.getint()
+            v_value = getint(v)
             if (v_value | self.or_mask) != v_value:
                 v = builder.do(rop.INT_OR, [v, ConstInt(self.or_mask)])
             v_second = v
@@ -395,9 +438,9 @@
             v_second = ConstFloat(r.random_float_storage())
         else:
             v_second = r.choice(builder.floatvars)
-        if abs(v_first.getfloat()) > 1E100 or abs(v_second.getfloat()) > 1E100:
+        if abs(getfloat(v_first)) > 1E100 or abs(getfloat(v_second)) > 1E100:
             raise CannotProduceOperation     # avoid infinities
-        if abs(v_second.getfloat()) < 1E-100:
+        if abs(getfloat(v_second)) < 1E-100:
             raise CannotProduceOperation     # e.g. division by zero error
         self.put(builder, [v_first, v_second])
 
@@ -432,7 +475,7 @@
         if not builder.floatvars:
             raise CannotProduceOperation
         box = r.choice(builder.floatvars)
-        if not (-sys.maxint-1 <= box.getfloat() <= sys.maxint):
+        if not (-sys.maxint-1 <= getfloat(box) <= sys.maxint):
             raise CannotProduceOperation      # would give an overflow
         self.put(builder, [box])
 
@@ -440,8 +483,8 @@
     def gen_guard(self, builder, r):
         v = builder.get_bool_var(r)
         op = ResOperation(self.opnum, [v])
-        passing = ((self.opnum == rop.GUARD_TRUE and v.getint()) or
-                   (self.opnum == rop.GUARD_FALSE and not v.getint()))
+        passing = ((self.opnum == rop.GUARD_TRUE and getint(v)) or
+                   (self.opnum == rop.GUARD_FALSE and not getint(v)))
         return op, passing
 
     def produce_into(self, builder, r):
@@ -459,8 +502,8 @@
             raise CannotProduceOperation
         box = r.choice(builder.ptrvars)[0]
         op = ResOperation(self.opnum, [box])
-        passing = ((self.opnum == rop.GUARD_NONNULL and box.getref_base()) or
-                   (self.opnum == rop.GUARD_ISNULL and not box.getref_base()))
+        passing = ((self.opnum == rop.GUARD_NONNULL and getref_base(box)) or
+                   (self.opnum == rop.GUARD_ISNULL and not getref_base(box)))
         return op, passing
 
 class GuardValueOperation(GuardOperation):
@@ -470,14 +513,14 @@
             other = r.choice(builder.intvars)
         else:
             if r.random() < 0.75:
-                value = v.getint()
+                value = getint(v)
             elif r.random() < 0.5:
-                value = v.getint() ^ 1
+                value = getint(v) ^ 1
             else:
                 value = r.random_integer()
             other = ConstInt(value)
         op = ResOperation(self.opnum, [v, other])
-        return op, (v.getint() == other.getint())
+        return op, (getint(v) == getint(other))
 
 # ____________________________________________________________
 
@@ -675,7 +718,7 @@
         assert not hasattr(loop, '_targettoken')
         for i in range(position):
             op = loop.operations[i]
-            if (not op.has_no_side_effect()
+            if (not rop.has_no_side_effect(op.opnum)
                     or op.type not in (INT, FLOAT)):
                 position = i
                 break       # cannot move the LABEL later
@@ -728,9 +771,9 @@
         self.expected = {}
         for v in endvars:
             if v.type == INT:
-                self.expected[v] = v.getint()
+                self.expected[v] = getint(v)
             elif v.type == FLOAT:
-                self.expected[v] = v.getfloatstorage()
+                self.expected[v] = getfloatstorage(v)
             else:
                 assert 0, v.type
 
@@ -742,7 +785,7 @@
             args = []
             for box in self.startvars:
                 if box not in self.loop.inputargs:
-                    box = box.constbox()
+                    box = constbox(box)
                 args.append(box)
             self.cpu.compile_loop(self.loop.inputargs,
                                   [ResOperation(rop.JUMP, args,
@@ -760,7 +803,7 @@
 
     def clear_state(self):
         for v, S, fields in self.prebuilt_ptr_consts:
-            container = v.getref_base()._obj.container
+            container = getref_base(v)._obj.container
             for name, value in fields.items():
                 if isinstance(name, str):
                     setattr(container, name, value)
@@ -781,9 +824,9 @@
         arguments = []
         for box in self.loop.inputargs:
             if box.type == INT:
-                arguments.append(box.getint())
+                arguments.append(getint(box))
             elif box.type == FLOAT:
-                arguments.append(box.getfloatstorage())
+                arguments.append(getfloatstorage(box))
             else:
                 assert 0, box.type
         deadframe = cpu.execute_token(self.runjitcelltoken(), *arguments)
@@ -795,7 +838,7 @@
             if v not in self.expected:
                 assert v.getopnum() == rop.SAME_AS_I   # special case
                 assert isinstance(v.getarg(0), ConstInt)
-                self.expected[v] = v.getarg(0).getint()
+                self.expected[v] = getint(v.getarg(0))
             if v.type == FLOAT:
                 value = cpu.get_float_value(deadframe, i)
             else:
@@ -807,7 +850,7 @@
                 )
         exc = cpu.grab_exc_value(deadframe)
         if (self.guard_op is not None and
-            self.guard_op.is_guard_exception()):
+            rop.is_guard_exception(self.guard_op.getopnum())):
             if self.guard_op.getopnum() == rop.GUARD_NO_EXCEPTION:
                 do_assert(exc,
                           "grab_exc_value() should not be %r" % (exc,))
@@ -840,7 +883,7 @@
         # generate the branch: a sequence of operations that ends in a FINISH
         subloop = DummyLoop([])
         self.subloops.append(subloop)   # keep around for debugging
-        if guard_op.is_guard_exception():
+        if rop.is_guard_exception(guard_op.getopnum()):
             subloop.operations.append(exc_handling(guard_op))
         bridge_builder = self.builder.fork(self.builder.cpu, subloop,
                                            op.getfailargs()[:])
@@ -876,9 +919,9 @@
             args = []
             for x in subset:
                 if x.type == INT:
-                    args.append(InputArgInt(x.getint()))
+                    args.append(InputArgInt(getint(x)))
                 elif x.type == FLOAT:
-                    args.append(InputArgFloat(x.getfloatstorage()))
+                    args.append(InputArgFloat(getfloatstorage(x)))
                 else:
                     assert 0, x.type
             rl = RandomLoop(self.builder.cpu, self.builder.fork,
diff --git a/rpython/jit/backend/x86/regalloc.py 
b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -358,11 +358,11 @@
             assert self.assembler.mc._frame_size == DEFAULT_FRAME_BYTES
             self.rm.position = i
             self.xrm.position = i
-            if op.has_no_side_effect() and op not in self.longevity:
+            if rop.has_no_side_effect(op.opnum) and op not in self.longevity:
                 i += 1
                 self.possibly_free_vars_for_op(op)
                 continue
-            if not we_are_translated() and op.getopnum() == -127:
+            if not we_are_translated() and op.getopnum() == rop.FORCE_SPILL:
                 self._consider_force_spill(op)
             else:
                 oplist[op.getopnum()](self, op)
diff --git a/rpython/jit/metainterp/blackhole.py 
b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1585,7 +1585,6 @@
     def _done_with_this_frame(self):
         # rare case: we only get there if the blackhole interps all returned
         # normally (in general we get a ContinueRunningNormally exception).
-        sd = self.builder.metainterp_sd
         kind = self._return_type
         if kind == 'v':
             raise jitexc.DoneWithThisFrameVoid()
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
@@ -27,12 +27,11 @@
 
 class CompileData(object):
     memo = None
+    log_noopt = True
     
     def forget_optimization_info(self):
-        for arg in self.start_label.getarglist():
+        for arg in self.trace.inputargs:
             arg.set_forwarded(None)
-        for op in self.operations:
-            op.set_forwarded(None)
 
 class LoopCompileData(CompileData):
     """ An object that accumulates all of the necessary info for
@@ -40,15 +39,13 @@
 
     This is the case of label() ops label()
     """
-    def __init__(self, start_label, end_label, operations,
-                 call_pure_results=None, enable_opts=None):
-        self.start_label = start_label
-        self.end_label = end_label
+    def __init__(self, trace, runtime_boxes, call_pure_results=None,
+                 enable_opts=None):
         self.enable_opts = enable_opts
-        assert start_label.getopnum() == rop.LABEL
-        assert end_label.getopnum() == rop.LABEL
-        self.operations = operations
+        self.trace = trace
         self.call_pure_results = call_pure_results
+        assert runtime_boxes is not None
+        self.runtime_boxes = runtime_boxes
 
     def optimize(self, metainterp_sd, jitdriver_sd, optimizations, unroll):
         from rpython.jit.metainterp.optimizeopt.unroll import (UnrollOptimizer,
@@ -56,23 +53,21 @@
 
         if unroll:
             opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations)
-            return opt.optimize_preamble(self.start_label, self.end_label,
-                                         self.operations,
+            return opt.optimize_preamble(self.trace,
+                                         self.runtime_boxes,
                                          self.call_pure_results,
                                          self.box_names_memo)
         else:
             opt = Optimizer(metainterp_sd, jitdriver_sd, optimizations)
-            return opt.propagate_all_forward(self.start_label.getarglist(),
-               self.operations, self.call_pure_results)
+            return opt.propagate_all_forward(self.trace, 
self.call_pure_results)
 
 class SimpleCompileData(CompileData):
     """ This represents label() ops jump with no extra info associated with
     the label
     """
-    def __init__(self, start_label, operations, call_pure_results=None,
+    def __init__(self, trace, call_pure_results=None,
                  enable_opts=None):
-        self.start_label = start_label
-        self.operations = operations
+        self.trace = trace
         self.call_pure_results = call_pure_results
         self.enable_opts = enable_opts
 
@@ -81,17 +76,17 @@
 
         #assert not unroll
         opt = Optimizer(metainterp_sd, jitdriver_sd, optimizations)
-        return opt.propagate_all_forward(self.start_label.getarglist(),
-            self.operations, self.call_pure_results)
+        return opt.propagate_all_forward(self.trace.get_iter(),
+            self.call_pure_results)
 
 class BridgeCompileData(CompileData):
     """ 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, start_label, operations, call_pure_results=None,
+    def __init__(self, trace, runtime_boxes, call_pure_results=None,
                  enable_opts=None, inline_short_preamble=False):
-        self.start_label = start_label
-        self.operations = operations
+        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
@@ -100,7 +95,7 @@
         from rpython.jit.metainterp.optimizeopt.unroll import UnrollOptimizer
 
         opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations)
-        return opt.optimize_bridge(self.start_label, self.operations,
+        return opt.optimize_bridge(self.trace, self.runtime_boxes,
                                    self.call_pure_results,
                                    self.inline_short_preamble,
                                    self.box_names_memo)
@@ -109,12 +104,13 @@
     """ This represents label() ops jump with extra info that's from the
     run of LoopCompileData. Jump goes to the same label
     """
-    def __init__(self, start_label, end_jump, operations, state,
+    log_noopt = False
+
+    def __init__(self, trace, celltoken, state,
                  call_pure_results=None, enable_opts=None,
                  inline_short_preamble=True):
-        self.start_label = start_label
-        self.end_jump = end_jump
-        self.operations = operations
+        self.trace = trace
+        self.celltoken = celltoken
         self.enable_opts = enable_opts
         self.state = state
         self.call_pure_results = call_pure_results
@@ -125,9 +121,8 @@
 
         assert unroll # we should not be here if it's disabled
         opt = UnrollOptimizer(metainterp_sd, jitdriver_sd, optimizations)
-        return opt.optimize_peeled_loop(self.start_label, self.end_jump,
-            self.operations, self.state, self.call_pure_results,
-            self.inline_short_preamble)
+        return opt.optimize_peeled_loop(self.trace, self.celltoken, self.state,
+            self.call_pure_results, self.inline_short_preamble)
 
 def show_procedures(metainterp_sd, procedure=None, error=None):
     # debugging
@@ -208,23 +203,21 @@
 # ____________________________________________________________
 
 
-def compile_simple_loop(metainterp, greenkey, start, inputargs, ops, jumpargs,
-                        enable_opts):
+def compile_simple_loop(metainterp, greenkey, trace, runtime_args, enable_opts,
+                        cut_at):
     from rpython.jit.metainterp.optimizeopt import optimize_trace
 
     jitdriver_sd = metainterp.jitdriver_sd
     metainterp_sd = metainterp.staticdata
     jitcell_token = make_jitcell_token(jitdriver_sd)
-    label = ResOperation(rop.LABEL, inputargs[:], descr=jitcell_token)
-    jump_op = ResOperation(rop.JUMP, jumpargs[:], descr=jitcell_token)
     call_pure_results = metainterp.call_pure_results
-    data = SimpleCompileData(label, ops + [jump_op],
-                                 call_pure_results=call_pure_results,
-                                 enable_opts=enable_opts)
+    data = SimpleCompileData(trace, call_pure_results=call_pure_results,
+                             enable_opts=enable_opts)
     try:
         loop_info, ops = optimize_trace(metainterp_sd, jitdriver_sd,
                                         data, metainterp.box_names_memo)
     except InvalidLoop:
+        trace.cut_at(cut_at)
         return None
     loop = create_empty_loop(metainterp)
     loop.original_jitcell_token = jitcell_token
@@ -241,7 +234,7 @@
         loop.check_consistency()
     jitcell_token.target_tokens = [target_token]
     send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, "loop",
-                         inputargs, metainterp.box_names_memo)
+                         runtime_args, metainterp.box_names_memo)
     record_loop_or_bridge(metainterp_sd, loop)
     return target_token
 
@@ -255,6 +248,7 @@
     metainterp_sd = metainterp.staticdata
     jitdriver_sd = metainterp.jitdriver_sd
     history = metainterp.history
+    trace = history.trace
     warmstate = jitdriver_sd.warmstate
 
     enable_opts = jitdriver_sd.warmstate.enable_opts
@@ -264,16 +258,16 @@
         enable_opts = enable_opts.copy()
         del enable_opts['unroll']
 
-    ops = history.operations[start:]
+    jitcell_token = make_jitcell_token(jitdriver_sd)
+    cut_at = history.get_trace_position()
+    history.record(rop.JUMP, jumpargs, None, descr=jitcell_token)
+    if start != (0, 0, 0):
+        trace = trace.cut_trace_from(start, inputargs)
     if 'unroll' not in enable_opts or not 
metainterp.cpu.supports_guard_gc_type:
-        return compile_simple_loop(metainterp, greenkey, start, inputargs, ops,
-                                   jumpargs, enable_opts)
-    jitcell_token = make_jitcell_token(jitdriver_sd)
-    label = ResOperation(rop.LABEL, inputargs,
-                         descr=TargetToken(jitcell_token))
-    end_label = ResOperation(rop.LABEL, jumpargs, descr=jitcell_token)
+        return compile_simple_loop(metainterp, greenkey, trace, jumpargs,
+                                   enable_opts, cut_at)
     call_pure_results = metainterp.call_pure_results
-    preamble_data = LoopCompileData(label, end_label, ops,
+    preamble_data = LoopCompileData(trace, jumpargs,
                                     call_pure_results=call_pure_results,
                                     enable_opts=enable_opts)
     try:
@@ -281,17 +275,15 @@
                                                    preamble_data,
                                                    metainterp.box_names_memo)
     except InvalidLoop:
+        history.cut(cut_at)
         return None
 
     metainterp_sd = metainterp.staticdata
     jitdriver_sd = metainterp.jitdriver_sd
-    end_label = ResOperation(rop.LABEL, inputargs,
-                             descr=jitcell_token)
-    jump_op = ResOperation(rop.JUMP, jumpargs, descr=jitcell_token)
     start_descr = TargetToken(jitcell_token,
                               original_jitcell_token=jitcell_token)
     jitcell_token.target_tokens = [start_descr]
-    loop_data = UnrolledLoopData(end_label, jump_op, ops, start_state,
+    loop_data = UnrolledLoopData(trace, jitcell_token, start_state,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
@@ -299,11 +291,12 @@
                                              loop_data,
                                              metainterp.box_names_memo)
     except InvalidLoop:
+        history.cut(cut_at)
         return None
 
     if ((warmstate.vec and jitdriver_sd.vec) or warmstate.vec_all):
         from rpython.jit.metainterp.optimizeopt.vector import optimize_vector
-        loop_info, loop_ops = optimize_vector(metainterp_sd,
+        loop_info, loop_ops = optimize_vector(trace, metainterp_sd,
                                               jitdriver_sd, warmstate,
                                               loop_info, loop_ops,
                                               jitcell_token)
@@ -342,22 +335,20 @@
     to the first operation.
     """
     from rpython.jit.metainterp.optimizeopt import optimize_trace
-    from rpython.jit.metainterp.optimizeopt.optimizer import BasicLoopInfo
 
-    history = metainterp.history
+    trace = metainterp.history.trace.cut_trace_from(start, inputargs)
     metainterp_sd = metainterp.staticdata
     jitdriver_sd = metainterp.jitdriver_sd
+    history = metainterp.history
 
     loop_jitcell_token = metainterp.get_procedure_token(greenkey)
     assert loop_jitcell_token
 
-    end_label = ResOperation(rop.LABEL, inputargs[:],
-                             descr=loop_jitcell_token)
-    jump_op = ResOperation(rop.JUMP, jumpargs[:], descr=loop_jitcell_token)
+    cut = history.get_trace_position()
+    history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token)
     enable_opts = jitdriver_sd.warmstate.enable_opts
-    ops = history.operations[start:]
     call_pure_results = metainterp.call_pure_results
-    loop_data = UnrolledLoopData(end_label, jump_op, ops, start_state,
+    loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
@@ -366,8 +357,9 @@
                                              metainterp.box_names_memo)
     except InvalidLoop:
         # Fall back on jumping directly to preamble
-        jump_op = ResOperation(rop.JUMP, inputargs[:], 
descr=loop_jitcell_token)
-        loop_data = UnrolledLoopData(end_label, jump_op, [jump_op], 
start_state,
+        history.cut(cut)
+        history.record(rop.JUMP, jumpargs[:], None, descr=loop_jitcell_token)
+        loop_data = UnrolledLoopData(trace, loop_jitcell_token, start_state,
                                      call_pure_results=call_pure_results,
                                      enable_opts=enable_opts,
                                      inline_short_preamble=False)
@@ -376,9 +368,13 @@
                                                  loop_data,
                                                  metainterp.box_names_memo)
         except InvalidLoop:
+            history.cut(cut)
             return None
 
-    label_token = loop_info.label_op.getdescr()
+    label_op = loop_info.label_op
+    if label_op is None:
+        assert False, "unreachable code" # hint for some strange tests
+    label_token = label_op.getdescr()
     assert isinstance(label_token, TargetToken)
     if label_token.short_preamble:
         metainterp_sd.logger_ops.log_short_preamble([],
@@ -445,13 +441,13 @@
         box = inputargs[i]
         opnum = OpHelpers.getfield_for_descr(descr)
         emit_op(extra_ops,
-                ResOperation(opnum, [vable_box], descr))
+                ResOperation(opnum, [vable_box], descr=descr))
         box.set_forwarded(extra_ops[-1])
         i += 1
     arrayindex = 0
     for descr in vinfo.array_field_descrs:
         arraylen = vinfo.get_array_length(vable, arrayindex)
-        arrayop = ResOperation(rop.GETFIELD_GC_R, [vable_box], descr)
+        arrayop = ResOperation(rop.GETFIELD_GC_R, [vable_box], descr=descr)
         emit_op(extra_ops, arrayop)
         arraydescr = vinfo.array_descrs[arrayindex]
         assert i + arraylen <= len(inputargs)
@@ -1017,9 +1013,9 @@
         metainterp_sd.stats.add_jitcell_token(jitcell_token)
 
 
-def compile_trace(metainterp, resumekey):
+def compile_trace(metainterp, resumekey, runtime_boxes):
     """Try to compile a new bridge leading from the beginning of the history
-    to some existing place.
+    to some existging place.
     """
 
     from rpython.jit.metainterp.optimizeopt import optimize_trace
@@ -1037,20 +1033,19 @@
     else:
         inline_short_preamble = True
     inputargs = metainterp.history.inputargs[:]
-    operations = metainterp.history.operations
-    label = ResOperation(rop.LABEL, inputargs)
+    trace = metainterp.history.trace
     jitdriver_sd = metainterp.jitdriver_sd
     enable_opts = jitdriver_sd.warmstate.enable_opts
 
     call_pure_results = metainterp.call_pure_results
 
-    if operations[-1].getopnum() == rop.JUMP:
-        data = BridgeCompileData(label, operations[:],
+    if metainterp.history.ends_with_jump:
+        data = BridgeCompileData(trace, runtime_boxes,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts,
                                  inline_short_preamble=inline_short_preamble)
     else:
-        data = SimpleCompileData(label, operations[:],
+        data = SimpleCompileData(trace,
                                  call_pure_results=call_pure_results,
                                  enable_opts=enable_opts)
     try:
diff --git a/rpython/jit/metainterp/executor.py 
b/rpython/jit/metainterp/executor.py
--- a/rpython/jit/metainterp/executor.py
+++ b/rpython/jit/metainterp/executor.py
@@ -9,7 +9,7 @@
 from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID, AbstractDescr
 from rpython.jit.metainterp.history import ConstInt, ConstFloat, ConstPtr
 from rpython.jit.metainterp import resoperation
-from rpython.jit.metainterp.resoperation import rop
+from rpython.jit.metainterp.resoperation import rop, opname
 from rpython.jit.metainterp.blackhole import BlackholeInterpreter, NULL
 from rpython.jit.codewriter import longlong
 
@@ -314,7 +314,8 @@
 
 def _make_execute_list():
     execute_by_num_args = {}
-    for key, value in rop.__dict__.items():
+    for key in opname.values():
+        value = getattr(rop, key)
         if not key.startswith('_'):
             if (rop._FINAL_FIRST <= value <= rop._FINAL_LAST or
                 rop._GUARD_FIRST <= value <= rop._GUARD_LAST):
@@ -384,6 +385,11 @@
                          rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME,
                          rop.NURSERY_PTR_INCREMENT,
                          rop.LABEL,
+                         rop.ESCAPE_I,
+                         rop.ESCAPE_N,
+                         rop.ESCAPE_R,
+                         rop.ESCAPE_F,
+                         rop.FORCE_SPILL,
                          rop.SAVE_EXC_CLASS,
                          rop.SAVE_EXCEPTION,
                          rop.RESTORE_EXCEPTION,
diff --git a/rpython/jit/metainterp/graphpage.py 
b/rpython/jit/metainterp/graphpage.py
--- a/rpython/jit/metainterp/graphpage.py
+++ b/rpython/jit/metainterp/graphpage.py
@@ -170,7 +170,8 @@
         while True:
             op = operations[opindex]
             op_repr = op.repr(self.memo, graytext=True)
-            if op.getopnum() == rop.DEBUG_MERGE_POINT:
+            if (op.getopnum() == rop.DEBUG_MERGE_POINT and
+                    self.metainterp_sd is not None):
                 jd_sd = self.metainterp_sd.jitdrivers_sd[op.getarg(0).getint()]
                 if jd_sd._get_printable_location_ptr:
                     s = jd_sd.warmstate.get_location_str(op.getarglist()[3:])
diff --git a/rpython/jit/metainterp/heapcache.py 
b/rpython/jit/metainterp/heapcache.py
--- a/rpython/jit/metainterp/heapcache.py
+++ b/rpython/jit/metainterp/heapcache.py
@@ -1,33 +1,59 @@
-from rpython.jit.metainterp.history import ConstInt
+from rpython.jit.metainterp.history import Const, ConstInt
+from rpython.jit.metainterp.history import FrontendOp, RefFrontendOp
 from rpython.jit.metainterp.resoperation import rop, OpHelpers
+from rpython.jit.metainterp.executor import constant_from_op
+from rpython.rlib.rarithmetic import r_uint32, r_uint
+from rpython.rlib.objectmodel import always_inline
 
-class HeapCacheValue(object):
-    def __init__(self, box):
-        self.box = box
-        self.likely_virtual = False
-        self.reset_keep_likely_virtual()
+""" A big note: we don't do heap caches on Consts, because it used
+to be done with the identity of the Const instance. This gives very wonky
+results at best, so we decided to not do it at all. Can be fixed with
+interning of Consts (already done on trace anyway)
+"""
 
-    def reset_keep_likely_virtual(self):
-        self.known_class = False
-        self.known_nullity = False
-        # did we see the allocation during tracing?
-        self.seen_allocation = False
-        self.is_unescaped = False
-        self.nonstandard_virtualizable = False
-        self.length = None
-        self.dependencies = None
+# RefFrontendOp._heapc_flags:
+HF_LIKELY_VIRTUAL  = 0x01
+HF_KNOWN_CLASS     = 0x02
+HF_KNOWN_NULLITY   = 0x04
+HF_SEEN_ALLOCATION = 0x08   # did we see the allocation during tracing?
+HF_IS_UNESCAPED    = 0x10
+HF_NONSTD_VABLE    = 0x20
 
-    def __repr__(self):
-        return 'HeapCacheValue(%s)' % (self.box, )
+_HF_VERSION_INC    = 0x40   # must be last
+_HF_VERSION_MAX    = r_uint(2 ** 32 - _HF_VERSION_INC)
+
+@always_inline
+def add_flags(ref_frontend_op, flags):
+    f = ref_frontend_op._get_heapc_flags()
+    f |= r_uint(flags)
+    ref_frontend_op._set_heapc_flags(f)
+
+@always_inline
+def remove_flags(ref_frontend_op, flags):
+    f = ref_frontend_op._get_heapc_flags()
+    f &= r_uint(~flags)
+    ref_frontend_op._set_heapc_flags(f)
+
+@always_inline
+def test_flags(ref_frontend_op, flags):
+    f = ref_frontend_op._get_heapc_flags()
+    return bool(f & r_uint(flags))
+
+def maybe_replace_with_const(box):
+    if not isinstance(box, Const) and box.is_replaced_with_const():
+        return constant_from_op(box)
+    else:
+        return box
 
 
 class CacheEntry(object):
-    def __init__(self):
-        # both are {from_value: to_value} dicts
+    def __init__(self, heapcache):
+        # both are {from_ref_box: to_field_box} dicts
         # the first is for boxes where we did not see the allocation, the
         # second for anything else. the reason that distinction makes sense is
         # because if we saw the allocation, we know it cannot alias with
         # anything else where we saw the allocation.
+        self.heapcache = heapcache
         self.cache_anything = {}
         self.cache_seen_allocation = {}
 
@@ -36,112 +62,137 @@
             self.cache_seen_allocation.clear()
         self.cache_anything.clear()
 
-    def _getdict(self, value):
-        if value.seen_allocation:
+    def _seen_alloc(self, ref_box):
+        if not isinstance(ref_box, RefFrontendOp):
+            return False
+        return self.heapcache._check_flag(ref_box, HF_SEEN_ALLOCATION)
+
+    def _getdict(self, seen_alloc):
+        if seen_alloc:
             return self.cache_seen_allocation
         else:
             return self.cache_anything
 
-    def do_write_with_aliasing(self, value, fieldvalue):
-        self._clear_cache_on_write(value.seen_allocation)
-        self._getdict(value)[value] = fieldvalue
+    def do_write_with_aliasing(self, ref_box, fieldbox):
+        seen_alloc = self._seen_alloc(ref_box)
+        self._clear_cache_on_write(seen_alloc)
+        self._getdict(seen_alloc)[ref_box] = fieldbox
 
-    def read(self, value):
-        return self._getdict(value).get(value, None)
+    def read(self, ref_box):
+        dict = self._getdict(self._seen_alloc(ref_box))
+        try:
+            res_box = dict[ref_box]
+        except KeyError:
+            return None
+        return maybe_replace_with_const(res_box)
 
-    def read_now_known(self, value, fieldvalue):
-        self._getdict(value)[value] = fieldvalue
+    def read_now_known(self, ref_box, fieldbox):
+        self._getdict(self._seen_alloc(ref_box))[ref_box] = fieldbox
 
     def invalidate_unescaped(self):
         self._invalidate_unescaped(self.cache_anything)
         self._invalidate_unescaped(self.cache_seen_allocation)
 
     def _invalidate_unescaped(self, d):
-        for value in d.keys():
-            if not value.is_unescaped:
-                del d[value]
+        for ref_box in d.keys():
+            if not self.heapcache.is_unescaped(ref_box):
+                del d[ref_box]
 
 
 class FieldUpdater(object):
-    def __init__(self, heapcache, value, cache, fieldvalue):
-        self.heapcache = heapcache
-        self.value = value
+    def __init__(self, ref_box, cache, fieldbox):
+        self.ref_box = ref_box
         self.cache = cache
-        if fieldvalue is not None:
-            self.currfieldbox = fieldvalue.box
-        else:
-            self.currfieldbox = None
+        self.currfieldbox = fieldbox     # <= read directly from pyjitpl.py
 
     def getfield_now_known(self, fieldbox):
-        fieldvalue = self.heapcache.getvalue(fieldbox)
-        self.cache.read_now_known(self.value, fieldvalue)
+        self.cache.read_now_known(self.ref_box, fieldbox)
 
     def setfield(self, fieldbox):
-        fieldvalue = self.heapcache.getvalue(fieldbox)
-        self.cache.do_write_with_aliasing(self.value, fieldvalue)
+        self.cache.do_write_with_aliasing(self.ref_box, fieldbox)
+
+class DummyFieldUpdater(FieldUpdater):
+    def __init__(self):
+        self.currfieldbox = None
+
+    def getfield_now_known(self, fieldbox):
+        pass
+
+    def setfield(self, fieldbox):
+        pass
+
+dummy_field_updater = DummyFieldUpdater()
 
 
 class HeapCache(object):
     def __init__(self):
+        # Works with flags stored on RefFrontendOp._heapc_flags.
+        # There are two ways to do a global resetting of these flags:
+        # reset() and reset_keep_likely_virtual().  The basic idea is
+        # to use a version number in each RefFrontendOp, and in order
+        # to reset the flags globally, we increment the global version
+        # number in this class.  Then when we read '_heapc_flags' we
+        # also check if the associated version number is up-to-date
+        # or not.  More precisely, we have two global version numbers
+        # here: 'head_version' and 'likely_virtual_version'.  Normally
+        # we use 'head_version'.  For is_likely_virtual() though, we
+        # use the other, older version number.
+        self.head_version = r_uint(0)
+        self.likely_virtual_version = r_uint(0)
         self.reset()
 
     def reset(self):
-        # maps boxes to values
-        self.values = {}
-        # store the boxes that contain newly allocated objects, this maps the
-        # boxes to a bool, the bool indicates whether or not the object has
-        # escaped the trace or not (True means the box never escaped, False
-        # means it did escape), its presences in the mapping shows that it was
-        # allocated inside the trace
-        #if trace_branch:
-            #self.new_boxes = {}
-        #    pass
-        #else:
-            #for box in self.new_boxes:
-            #    self.new_boxes[box] = False
-        #    pass
-        #if reset_virtuals:
-        #    self.likely_virtuals = {}      # only for jit.isvirtual()
-        # Tracks which boxes should be marked as escaped when the key box
-        # escapes.
-        #self.dependencies = {}
-
+        # Global reset of all flags.  Update both version numbers so
+        # that any access to '_heapc_flags' will be marked as outdated.
+        assert self.head_version < _HF_VERSION_MAX
+        self.head_version += _HF_VERSION_INC
+        self.likely_virtual_version = self.head_version
+        #
         # heap cache
         # maps descrs to CacheEntry
         self.heap_cache = {}
         # heap array cache
-        # maps descrs to {index: {from_value: to_value}} dicts
+        # maps descrs to {index: CacheEntry} dicts
         self.heap_array_cache = {}
 
     def reset_keep_likely_virtuals(self):
-        for value in self.values.itervalues():
-            value.reset_keep_likely_virtual()
+        # Update only 'head_version', but 'likely_virtual_version' remains
+        # at its older value.
+        assert self.head_version < _HF_VERSION_MAX
+        self.head_version += _HF_VERSION_INC
         self.heap_cache = {}
         self.heap_array_cache = {}
 
-    def getvalue(self, box, create=True):
-        value = self.values.get(box, None)
-        if not value and create:
-            value = self.values[box] = HeapCacheValue(box)
-        return value
+    @always_inline
+    def test_head_version(self, ref_frontend_op):
+        return ref_frontend_op._get_heapc_flags() >= self.head_version
 
-    def getvalues(self, boxes):
-        return [self.getvalue(box) for box in boxes]
+    @always_inline
+    def test_likely_virtual_version(self, ref_frontend_op):
+        return ref_frontend_op._get_heapc_flags() >= 
self.likely_virtual_version
+
+    def update_version(self, ref_frontend_op):
+        """Ensure the version of 'ref_frontend_op' is current.  If not,
+        it will update 'ref_frontend_op' (removing most flags currently set).
+        """
+        if not self.test_head_version(ref_frontend_op):
+            f = self.head_version
+            if (self.test_likely_virtual_version(ref_frontend_op) and
+                test_flags(ref_frontend_op, HF_LIKELY_VIRTUAL)):
+                f |= HF_LIKELY_VIRTUAL
+            ref_frontend_op._set_heapc_flags(f)
+            ref_frontend_op._heapc_deps = None
 
     def invalidate_caches(self, opnum, descr, argboxes):
         self.mark_escaped(opnum, descr, argboxes)
         self.clear_caches(opnum, descr, argboxes)
 
     def _escape_from_write(self, box, fieldbox):
-        value = self.getvalue(box, create=False)
-        fieldvalue = self.getvalue(fieldbox, create=False)
-        if (value is not None and value.is_unescaped and
-                fieldvalue is not None and fieldvalue.is_unescaped):
-            if value.dependencies is None:
-                value.dependencies = []
-            value.dependencies.append(fieldvalue)
-        elif fieldvalue is not None:
-            self._escape(fieldvalue)
+        if self.is_unescaped(box) and self.is_unescaped(fieldbox):
+            deps = self._get_deps(box)
+            deps.append(fieldbox)
+        elif fieldbox is not None:
+            self._escape_box(fieldbox)
 
     def mark_escaped(self, opnum, descr, argboxes):
         if opnum == rop.SETFIELD_GC:
@@ -176,19 +227,20 @@
                 self._escape_box(box)
 
     def _escape_box(self, box):
-        value = self.getvalue(box, create=False)
-        if not value:
-            return
-        self._escape(value)
-
-    def _escape(self, value):
-        value.is_unescaped = False
-        value.likely_virtual = False
-        deps = value.dependencies
-        value.dependencies = None
-        if deps is not None:
-            for dep in deps:
-                self._escape(dep)
+        if isinstance(box, RefFrontendOp):
+            remove_flags(box, HF_LIKELY_VIRTUAL | HF_IS_UNESCAPED)
+            deps = box._heapc_deps
+            if deps is not None:
+                if not self.test_head_version(box):
+                    box._heapc_deps = None
+                else:
+                    # 'deps[0]' is abused to store the array length, keep it
+                    if deps[0] is None:
+                        box._heapc_deps = None
+                    else:
+                        box._heapc_deps = [deps[0]]
+                    for i in range(1, len(deps)):
+                        self._escape_box(deps[i])
 
     def clear_caches(self, opnum, descr, argboxes):
         if (opnum == rop.SETFIELD_GC or
@@ -241,7 +293,8 @@
         self.reset_keep_likely_virtuals()
 
     def _clear_caches_arraycopy(self, opnum, desrc, argboxes, effectinfo):
-        seen_allocation_of_target = self.getvalue(argboxes[2]).seen_allocation
+        seen_allocation_of_target = self._check_flag(
+                                            argboxes[2], HF_SEEN_ALLOCATION)
         if (
             isinstance(argboxes[3], ConstInt) and
             isinstance(argboxes[4], ConstInt) and
@@ -285,74 +338,82 @@
             return
         self.reset_keep_likely_virtuals()
 
+    def _get_deps(self, box):
+        if not isinstance(box, RefFrontendOp):
+            return None
+        self.update_version(box)
+        if box._heapc_deps is None:
+            box._heapc_deps = [None]
+        return box._heapc_deps
+
+    def _check_flag(self, box, flag):
+        return (isinstance(box, RefFrontendOp) and
+                    self.test_head_version(box) and
+                    test_flags(box, flag))
+
+    def _set_flag(self, box, flag):
+        assert isinstance(box, RefFrontendOp)
+        self.update_version(box)
+        add_flags(box, flag)
+
     def is_class_known(self, box):
-        value = self.getvalue(box, create=False)
-        if value:
-            return value.known_class
-        return False
+        return self._check_flag(box, HF_KNOWN_CLASS)
 
     def class_now_known(self, box):
-        self.getvalue(box).known_class = True
+        if isinstance(box, Const):
+            return
+        self._set_flag(box, HF_KNOWN_CLASS)
 
     def is_nullity_known(self, box):
-        value = self.getvalue(box, create=False)
-        if value:
-            return value.known_nullity
-        return False
+        if isinstance(box, Const):
+            return bool(box.getref_base())
+        return self._check_flag(box, HF_KNOWN_NULLITY)
 
     def nullity_now_known(self, box):
-        self.getvalue(box).known_nullity = True
+        if isinstance(box, Const):
+            return
+        self._set_flag(box, HF_KNOWN_NULLITY)
 
     def is_nonstandard_virtualizable(self, box):
-        value = self.getvalue(box, create=False)
-        if value:
-            return value.nonstandard_virtualizable
-        return False
+        return self._check_flag(box, HF_NONSTD_VABLE)
 
     def nonstandard_virtualizables_now_known(self, box):
-        self.getvalue(box).nonstandard_virtualizable = True
+        self._set_flag(box, HF_NONSTD_VABLE)
 
     def is_unescaped(self, box):
-        value = self.getvalue(box, create=False)
-        if value:
-            return value.is_unescaped
-        return False
+        return self._check_flag(box, HF_IS_UNESCAPED)
 
     def is_likely_virtual(self, box):
-        value = self.getvalue(box, create=False)
-        if value:
-            return value.likely_virtual
-        return False
+        # note: this is different from _check_flag()
+        return (isinstance(box, RefFrontendOp) and
+                self.test_likely_virtual_version(box) and
+                test_flags(box, HF_LIKELY_VIRTUAL))
 
     def new(self, box):
-        value = self.getvalue(box)
-        value.is_unescaped = True
-        value.likely_virtual = True
-        value.seen_allocation = True
+        assert isinstance(box, RefFrontendOp)
+        self.update_version(box)
+        add_flags(box, HF_LIKELY_VIRTUAL | HF_SEEN_ALLOCATION | 
HF_IS_UNESCAPED)
 
     def new_array(self, box, lengthbox):
         self.new(box)
         self.arraylen_now_known(box, lengthbox)
 
     def getfield(self, box, descr):
-        value = self.getvalue(box, create=False)
-        if value:
-            cache = self.heap_cache.get(descr, None)
-            if cache:
-                tovalue = cache.read(value)
-                if tovalue:
-                    return tovalue.box
+        cache = self.heap_cache.get(descr, None)
+        if cache:
+            return cache.read(box)
         return None
 
     def get_field_updater(self, box, descr):
-        value = self.getvalue(box)
+        if not isinstance(box, RefFrontendOp):
+            return dummy_field_updater
         cache = self.heap_cache.get(descr, None)
         if cache is None:
-            cache = self.heap_cache[descr] = CacheEntry()
-            fieldvalue = None
+            cache = self.heap_cache[descr] = CacheEntry(self)
+            fieldbox = None
         else:
-            fieldvalue = cache.read(value)
-        return FieldUpdater(self, value, cache, fieldvalue)
+            fieldbox = cache.read(box)
+        return FieldUpdater(box, cache, fieldbox)
 
     def getfield_now_known(self, box, descr, fieldbox):
         upd = self.get_field_updater(box, descr)
@@ -365,17 +426,12 @@
     def getarrayitem(self, box, indexbox, descr):
         if not isinstance(indexbox, ConstInt):
             return None
-        value = self.getvalue(box, create=False)
-        if value is None:
-            return None
         index = indexbox.getint()
         cache = self.heap_array_cache.get(descr, None)
         if cache:
             indexcache = cache.get(index, None)
             if indexcache is not None:
-                resvalue = indexcache.read(value)
-                if resvalue:
-                    return resvalue.box
+                return indexcache.read(box)
         return None
 
     def _get_or_make_array_cache_entry(self, indexbox, descr):
@@ -385,16 +441,14 @@
         cache = self.heap_array_cache.setdefault(descr, {})
         indexcache = cache.get(index, None)
         if indexcache is None:
-            cache[index] = indexcache = CacheEntry()
+            cache[index] = indexcache = CacheEntry(self)
         return indexcache
 
 
     def getarrayitem_now_known(self, box, indexbox, fieldbox, descr):
-        value = self.getvalue(box)
-        fieldvalue = self.getvalue(fieldbox)
         indexcache = self._get_or_make_array_cache_entry(indexbox, descr)
         if indexcache:
-            indexcache.read_now_known(value, fieldvalue)
+            indexcache.read_now_known(box, fieldbox)
 
     def setarrayitem(self, box, indexbox, fieldbox, descr):
         if not isinstance(indexbox, ConstInt):
@@ -402,25 +456,31 @@
             if cache is not None:
                 cache.clear()
             return
-        value = self.getvalue(box)
-        fieldvalue = self.getvalue(fieldbox)
         indexcache = self._get_or_make_array_cache_entry(indexbox, descr)
         if indexcache:
-            indexcache.do_write_with_aliasing(value, fieldvalue)
+            indexcache.do_write_with_aliasing(box, fieldbox)
 
     def arraylen(self, box):
-        value = self.getvalue(box, create=False)
-        if value and value.length:
-            return value.length.box
+        if (isinstance(box, RefFrontendOp) and
+            self.test_head_version(box) and
+            box._heapc_deps is not None):
+            res_box = box._heapc_deps[0]
+            if res_box is not None:
+                return maybe_replace_with_const(res_box)
         return None
 
     def arraylen_now_known(self, box, lengthbox):
-        value = self.getvalue(box)
-        value.length = self.getvalue(lengthbox)
+        # we store in '_heapc_deps' a list of boxes: the *first* box is
+        # the known length or None, and the remaining boxes are the
+        # regular dependencies.
+        if isinstance(box, Const):
+            return
+        deps = self._get_deps(box)
+        assert deps is not None
+        deps[0] = lengthbox
 
     def replace_box(self, oldbox, newbox):
-        value = self.getvalue(oldbox, create=False)
-        if value is None:
-            return
-        value.box = newbox
-        self.values[newbox] = value
+        # here, only for replacing a box with a const
+        if isinstance(oldbox, FrontendOp) and isinstance(newbox, Const):
+            assert newbox.same_constant(constant_from_op(oldbox))
+            oldbox.set_replaced_with_const()
diff --git a/rpython/jit/metainterp/history.py 
b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -3,12 +3,17 @@
 from rpython.rlib.objectmodel import we_are_translated, Symbolic
 from rpython.rlib.objectmodel import compute_unique_id, specialize
 from rpython.rlib.rarithmetic import r_int64, is_valid_int
+from rpython.rlib.rarithmetic import LONG_BIT, intmask, r_uint
+from rpython.rlib.jit import Counters
 
 from rpython.conftest import option
 
-from rpython.jit.metainterp.resoperation import ResOperation, rop, 
AbstractValue
+from rpython.jit.metainterp.resoperation import ResOperation, rop,\
+    AbstractValue, oparity, AbstractResOp, IntOp, RefOp, FloatOp,\
+    opclasses
 from rpython.jit.codewriter import heaptracker, longlong
 import weakref
+from rpython.jit.metainterp import jitexc
 
 # ____________________________________________________________
 
@@ -22,6 +27,15 @@
 
 FAILARGS_LIMIT = 1000
 
+class SwitchToBlackhole(jitexc.JitException):
+    def __init__(self, reason, raising_exception=False):
+        self.reason = reason
+        self.raising_exception = raising_exception
+        # ^^^ must be set to True if the SwitchToBlackhole is raised at a
+        #     point where the exception on metainterp.last_exc_value
+        #     is supposed to be raised.  The default False means that it
+        #     should just be copied into the blackhole interp, but not raised.
+
 def getkind(TYPE, supports_floats=True,
                   supports_longlong=True,
                   supports_singlefloats=True):
@@ -72,57 +86,10 @@
                         ) #compute_unique_id(box))
 
 
-class XxxAbstractValue(object):
-    __slots__ = ()
-
-    def getint(self):
-        raise NotImplementedError
-
-    def getfloatstorage(self):
-        raise NotImplementedError
-
-    def getfloat(self):
-        return longlong.getrealfloat(self.getfloatstorage())
-
-    def getref_base(self):
-        raise NotImplementedError
-
-    def getref(self, TYPE):
-        raise NotImplementedError
-    getref._annspecialcase_ = 'specialize:arg(1)'
-
-    def constbox(self):
-        raise NotImplementedError
-
-    def getaddr(self):
-        "Only for raw addresses (BoxInt & ConstInt), not for GC addresses"
-        raise NotImplementedError
-
-    def sort_key(self):
-        raise NotImplementedError
-
-    def nonnull(self):
-        raise NotImplementedError
-
-    def repr_rpython(self):
-        return '%s' % self
-
-    def _get_str(self):
-        raise NotImplementedError
-
-    def same_box(self, other):
-        return self is other
-
-    def same_shape(self, other):
-        # only structured containers can compare their shape (vector box)
-        return True
-
-    def getaccum(self):
-        return None
-
 class AbstractDescr(AbstractValue):
-    __slots__ = ()
+    __slots__ = ('descr_index',)
     llopaque = True
+    descr_index = -1
 
     def repr_of_descr(self):
         return '%r' % (self,)
@@ -204,7 +171,7 @@
 
 
 class Const(AbstractValue):
-    __slots__ = ()
+    _attrs_ = ()
 
     @staticmethod
     def _new(x):
@@ -638,43 +605,174 @@
 # ____________________________________________________________
 
 
+FO_REPLACED_WITH_CONST = r_uint(1)
+FO_POSITION_SHIFT      = 1
+FO_POSITION_MASK       = r_uint(0xFFFFFFFE)
+
+
+class FrontendOp(AbstractResOp):
+    type = 'v'
+    _attrs_ = ('position_and_flags',)
+
+    def __init__(self, pos):
+        # p is the 32-bit position shifted left by one (might be negative,
+        # but casted to the 32-bit UINT type)
+        p = rffi.cast(rffi.UINT, pos << FO_POSITION_SHIFT)
+        self.position_and_flags = r_uint(p)    # zero-extended to a full word
+
+    def get_position(self):
+        # p is the signed 32-bit position, from self.position_and_flags
+        p = rffi.cast(rffi.INT, self.position_and_flags)
+        return intmask(p) >> FO_POSITION_SHIFT
+
+    def set_position(self, new_pos):
+        assert new_pos >= 0
+        self.position_and_flags &= ~FO_POSITION_MASK
+        self.position_and_flags |= r_uint(new_pos << FO_POSITION_SHIFT)
+
+    def is_replaced_with_const(self):
+        return bool(self.position_and_flags & FO_REPLACED_WITH_CONST)
+
+    def set_replaced_with_const(self):
+        self.position_and_flags |= FO_REPLACED_WITH_CONST
+
+    def __repr__(self):
+        return '%s(0x%x)' % (self.__class__.__name__, self.position_and_flags)
+
+class IntFrontendOp(IntOp, FrontendOp):
+    _attrs_ = ('position_and_flags', '_resint')
+
+    def copy_value_from(self, other):
+        self._resint = other.getint()
+
+class FloatFrontendOp(FloatOp, FrontendOp):
+    _attrs_ = ('position_and_flags', '_resfloat')
+
+    def copy_value_from(self, other):
+        self._resfloat = other.getfloatstorage()
+
+class RefFrontendOp(RefOp, FrontendOp):
+    _attrs_ = ('position_and_flags', '_resref', '_heapc_deps')
+    if LONG_BIT == 32:
+        _attrs_ += ('_heapc_flags',)   # on 64 bit, this gets stored into the
+        _heapc_flags = r_uint(0)       # high 32 bits of 'position_and_flags'
+    _heapc_deps = None
+
+    def copy_value_from(self, other):
+        self._resref = other.getref_base()
+
+    if LONG_BIT == 32:
+        def _get_heapc_flags(self):
+            return self._heapc_flags
+        def _set_heapc_flags(self, value):
+            self._heapc_flags = value
+    else:
+        def _get_heapc_flags(self):
+            return self.position_and_flags >> 32
+        def _set_heapc_flags(self, value):
+            self.position_and_flags = (
+                (self.position_and_flags & 0xFFFFFFFF) |
+                (value << 32))
+
+
 class History(object):
+    ends_with_jump = False
+    trace = None
+
     def __init__(self):
-        self.inputargs = None
-        self.operations = []
+        self.descr_cache = {}
+        self.descrs = {}
+        self.consts = []
+        self._cache = []
+
+    def set_inputargs(self, inpargs, metainterp_sd):
+        from rpython.jit.metainterp.opencoder import Trace
+
+        self.trace = Trace(inpargs, metainterp_sd)
+        self.inputargs = inpargs
+        if self._cache:
+            # hack to record the ops *after* we know our inputargs
+            for (opnum, argboxes, op, descr) in self._cache:
+                pos = self.trace.record_op(opnum, argboxes, descr)
+                op.set_position(pos)
+            self._cache = None
+
+    def length(self):
+        return self.trace._count - len(self.trace.inputargs)
+
+    def get_trace_position(self):
+        return self.trace.cut_point()
+
+    def cut(self, cut_at):
+        self.trace.cut_at(cut_at)
+
+    def any_operation(self):
+        return self.trace._count > self.trace._start
+
+    @specialize.argtype(2)
+    def set_op_value(self, op, value):
+        if value is None:
+            return        
+        elif isinstance(value, bool):
+            op.setint(int(value))
+        elif lltype.typeOf(value) == lltype.Signed:
+            op.setint(value)
+        elif lltype.typeOf(value) is longlong.FLOATSTORAGE:
+            op.setfloatstorage(value)
+        else:
+            assert lltype.typeOf(value) == llmemory.GCREF
+            op.setref_base(value)
+
+    def _record_op(self, opnum, argboxes, descr=None):
+        from rpython.jit.metainterp.opencoder import FrontendTagOverflow
+
+        try:
+            return self.trace.record_op(opnum, argboxes, descr)
+        except FrontendTagOverflow:
+            # note that with the default settings this one should not
+            # happen - however if we hit that case, we don't get
+            # anything disabled
+            raise SwitchToBlackhole(Counters.ABORT_TOO_LONG)
 
     @specialize.argtype(3)
     def record(self, opnum, argboxes, value, descr=None):
-        op = ResOperation(opnum, argboxes, descr)
+        if self.trace is None:
+            pos = 2**14 - 1
+        else:
+            pos = self._record_op(opnum, argboxes, descr)
         if value is None:
-            assert op.type == 'v'
+            op = FrontendOp(pos)
         elif isinstance(value, bool):
-            assert op.type == 'i'
-            op.setint(int(value))
+            op = IntFrontendOp(pos)
         elif lltype.typeOf(value) == lltype.Signed:
-            assert op.type == 'i'
-            op.setint(value)
+            op = IntFrontendOp(pos)
         elif lltype.typeOf(value) is longlong.FLOATSTORAGE:
-            assert op.type == 'f'
-            op.setfloatstorage(value)
+            op = FloatFrontendOp(pos)
         else:
-            assert lltype.typeOf(value) == llmemory.GCREF
-            assert op.type == 'r'
-            op.setref_base(value)
-        self.operations.append(op)
+            op = RefFrontendOp(pos)
+        if self.trace is None:
+            self._cache.append((opnum, argboxes, op, descr))
+        self.set_op_value(op, value)
         return op
 
+    def record_nospec(self, opnum, argboxes, descr=None):
+        tp = opclasses[opnum].type
+        pos = self._record_op(opnum, argboxes, descr)
+        if tp == 'v':
+            return FrontendOp(pos)
+        elif tp == 'i':
+            return IntFrontendOp(pos)
+        elif tp == 'f':
+            return FloatFrontendOp(pos)
+        assert tp == 'r'
+        return RefFrontendOp(pos)
+
     def record_default_val(self, opnum, argboxes, descr=None):
-        op = ResOperation(opnum, argboxes, descr)
-        assert op.is_same_as()
+        assert rop.is_same_as(opnum)
+        op = self.record_nospec(opnum, argboxes, descr)
         op.copy_value_from(argboxes[0])
-        self.operations.append(op)
         return op
 
-    def substitute_operation(self, position, opnum, argboxes, descr=None):
-        resbox = self.operations[position].result
-        op = ResOperation(opnum, argboxes, resbox, descr)
-        self.operations[position] = op
 
 # ____________________________________________________________
 
@@ -720,15 +818,15 @@
     compiled_count = 0
     enter_count = 0
     aborted_count = 0
-    operations = None
 
-    def __init__(self):
+    def __init__(self, metainterp_sd):
         self.loops = []
         self.locations = []
         self.aborted_keys = []
         self.invalidated_token_numbers = set()    # <- not RPython
         self.jitcell_token_wrefs = []
         self.jitcell_dicts = []                   # <- not RPython
+        self.metainterp_sd = metainterp_sd
 
     def clear(self):
         del self.loops[:]
@@ -747,7 +845,7 @@
         self.jitcell_token_wrefs.append(weakref.ref(token))
 
     def set_history(self, history):
-        self.operations = history.operations
+        self.history = history
 
     def aborted(self):
         self.aborted_count += 1
@@ -784,7 +882,9 @@
 
     def check_history(self, expected=None, **check):
         insns = {}
-        for op in self.operations:
+        t = self.history.trace.get_iter()
+        while not t.done():
+            op = t.next()
             opname = op.getopname()
             insns[opname] = insns.get(opname, 0) + 1
         if expected is not None:
diff --git a/rpython/jit/metainterp/logger.py b/rpython/jit/metainterp/logger.py
--- a/rpython/jit/metainterp/logger.py
+++ b/rpython/jit/metainterp/logger.py
@@ -12,6 +12,19 @@
         self.metainterp_sd = metainterp_sd
         self.guard_number = guard_number
 
+    def log_loop_from_trace(self, trace, memo):
+        if not have_debug_prints():
+            return
+        inputargs, ops = self._unpack_trace(trace)
+        self.log_loop(inputargs, ops, memo=memo)
+
+    def _unpack_trace(self, trace):
+        ops = []
+        i = trace.get_iter()
+        while not i.done():
+            ops.append(i.next())
+        return i.inputargs, ops
+
     def log_loop(self, inputargs, operations, number=0, type=None,
                  ops_offset=None, name='', memo=None):
         if type is None:
@@ -82,8 +95,11 @@
         debug_stop("jit-log-short-preamble")
         return logops
 
-    def log_abort_loop(self, inputargs, operations, memo=None):
+    def log_abort_loop(self, trace, memo=None):
         debug_start("jit-abort-log")
+        if not have_debug_prints():
+            return
+        inputargs, operations = self._unpack_trace(trace)
         logops = self._log_operations(inputargs, operations, ops_offset=None,
                                       memo=memo)
         debug_stop("jit-abort-log")
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to