[pypy-commit] pypy default: merge heads

arigo pypy.commits at gmail.com
Sun Mar 27 11:16:32 EDT 2016


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r83394:060cbab9d669
Date: 2016-03-27 17:15 +0200
http://bitbucket.org/pypy/pypy/changeset/060cbab9d669/

Log:	merge heads

diff too long, truncating to 2000 out of 8524 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/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -27,3 +27,8 @@
 .. branch: fix_transpose_for_list_v3
 
 Allow arguments to transpose to be sequences
+
+.. branch: jit-leaner-frontend
+
+Improve the tracing speed in the frontend as well as heapcache by using a more compact representation
+of traces
\ No newline at end of file
diff --git a/pypy/objspace/std/objectobject.py b/pypy/objspace/std/objectobject.py
--- a/pypy/objspace/std/objectobject.py
+++ b/pypy/objspace/std/objectobject.py
@@ -110,7 +110,7 @@
 def descr__init__(space, w_obj, __args__):
     # don't allow arguments unless __new__ is overridden
     w_type = space.type(w_obj)
-    w_parent_new, _ = w_type.lookup_where('__new__')
+    w_parent_new, _ = space.lookup_in_type_where(w_type, '__new__')
     if w_parent_new is space.w_object:
         try:
             __args__.fixedunpack(0)
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/arm/assembler.py b/rpython/jit/backend/arm/assembler.py
--- a/rpython/jit/backend/arm/assembler.py
+++ b/rpython/jit/backend/arm/assembler.py
@@ -939,9 +939,9 @@
             op = operations[i]
             self.mc.mark_op(op)
             opnum = op.getopnum()
-            if op.has_no_side_effect() and op not in regalloc.longevity:
+            if rop.has_no_side_effect(opnum) and op not in regalloc.longevity:
                 regalloc.possibly_free_vars_for_op(op)
-            elif not we_are_translated() and op.getopnum() == -127:
+            elif not we_are_translated() and op.getopnum() == rop.FORCE_SPILL:
                 regalloc.prepare_force_spill(op, fcond)
             else:
                 arglocs = regalloc_operations[opnum](regalloc, op, fcond)
@@ -949,7 +949,7 @@
                     fcond = asm_operations[opnum](self, op, arglocs,
                                                         regalloc, fcond)
                     assert fcond is not None
-            if op.is_guard():
+            if rop.is_guard(opnum):
                 regalloc.possibly_free_vars(op.getfailargs())
             if op.type != 'v':
                 regalloc.possibly_free_var(op)
diff --git a/rpython/jit/backend/arm/detect.py b/rpython/jit/backend/arm/detect.py
--- a/rpython/jit/backend/arm/detect.py
+++ b/rpython/jit/backend/arm/detect.py
@@ -1,6 +1,7 @@
 import os
 
 from rpython.translator.tool.cbuild import ExternalCompilationInfo
+from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.tool import rffi_platform
 from rpython.rlib.clibffi import FFI_DEFAULT_ABI, FFI_SYSV, FFI_VFP
 from rpython.translator.platform import CompilationError
@@ -15,6 +16,7 @@
     asm volatile("VMOV s0, s1");
 }
     """])
+getauxval = rffi.llexternal("getauxval", [lltype.Unsigned], lltype.Unsigned)
 
 def detect_hardfloat():
     return FFI_DEFAULT_ABI == FFI_VFP
@@ -63,3 +65,10 @@
                     "falling back to", "ARMv%d" % n)
     debug_stop("jit-backend-arch")
     return n
+
+
+def detect_neon():
+    AT_HWCAP = 16
+    HWCAP_NEON = 1 << 12
+    hwcap = getauxval(AT_HWCAP)
+    return bool(hwcap & HWCAP_NEON)
diff --git a/rpython/jit/backend/arm/opassembler.py b/rpython/jit/backend/arm/opassembler.py
--- a/rpython/jit/backend/arm/opassembler.py
+++ b/rpython/jit/backend/arm/opassembler.py
@@ -1092,8 +1092,8 @@
         self.mc.VCVT_int_to_float(res.value, r.svfp_ip.value)
         return fcond
 
-    # the following five instructions are only ARMv7;
-    # regalloc.py won't call them at all on ARMv6
+    # the following five instructions are only ARMv7 with NEON;
+    # regalloc.py won't call them at all, in other cases
     emit_opx_llong_add = gen_emit_float_op('llong_add', 'VADD_i64')
     emit_opx_llong_sub = gen_emit_float_op('llong_sub', 'VSUB_i64')
     emit_opx_llong_and = gen_emit_float_op('llong_and', 'VAND_i64')
diff --git a/rpython/jit/backend/arm/regalloc.py b/rpython/jit/backend/arm/regalloc.py
--- a/rpython/jit/backend/arm/regalloc.py
+++ b/rpython/jit/backend/arm/regalloc.py
@@ -530,7 +530,7 @@
                             EffectInfo.OS_LLONG_AND,
                             EffectInfo.OS_LLONG_OR,
                             EffectInfo.OS_LLONG_XOR):
-                if self.cpu.cpuinfo.arch_version >= 7:
+                if self.cpu.cpuinfo.neon:
                     args = self._prepare_llong_binop_xx(op, fcond)
                     self.perform_extra(op, args, fcond)
                     return
diff --git a/rpython/jit/backend/arm/runner.py b/rpython/jit/backend/arm/runner.py
--- a/rpython/jit/backend/arm/runner.py
+++ b/rpython/jit/backend/arm/runner.py
@@ -7,13 +7,14 @@
 from rpython.rlib.jit_hooks import LOOP_RUN_CONTAINER
 from rpython.rtyper.lltypesystem import lltype, llmemory
 from rpython.jit.backend.arm.detect import detect_hardfloat
-from rpython.jit.backend.arm.detect import detect_arch_version
+from rpython.jit.backend.arm.detect import detect_arch_version, detect_neon
 
 jitframe.STATICSIZE = JITFRAME_FIXED_SIZE
 
 class CPUInfo(object):
     hf_abi = False
     arch_version = 6
+    neon = False
 
 class AbstractARMCPU(AbstractLLCPU):
 
@@ -48,6 +49,7 @@
     def setup_once(self):
         self.cpuinfo.arch_version = detect_arch_version()
         self.cpuinfo.hf_abi = detect_hardfloat()
+        self.cpuinfo.neon = detect_neon()
         #self.codemap.setup()
         self.assembler.setup_once()
 
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/ppc/regalloc.py b/rpython/jit/backend/ppc/regalloc.py
--- a/rpython/jit/backend/ppc/regalloc.py
+++ b/rpython/jit/backend/ppc/regalloc.py
@@ -286,7 +286,8 @@
             self.assembler.mc.mark_op(op)
             self.rm.position = i
             self.fprm.position = i
-            if op.has_no_side_effect() and op not in self.longevity:
+            opnum = op.opnum
+            if rop.has_no_side_effect(opnum) and op not in self.longevity:
                 i += 1
                 self.possibly_free_vars_for_op(op)
                 continue
@@ -298,8 +299,7 @@
                 else:
                     self.fprm.temp_boxes.append(box)
             #
-            opnum = op.getopnum()
-            if not we_are_translated() and opnum == -127:
+            if not we_are_translated() and opnum == rop.FORCE_SPILL:
                 self._consider_force_spill(op)
             else:
                 arglocs = oplist[opnum](self, op)
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/backend/zarch/regalloc.py b/rpython/jit/backend/zarch/regalloc.py
--- a/rpython/jit/backend/zarch/regalloc.py
+++ b/rpython/jit/backend/zarch/regalloc.py
@@ -476,7 +476,8 @@
             self.assembler.mc.mark_op(op)
             self.rm.position = i
             self.fprm.position = i
-            if op.has_no_side_effect() and op not in self.longevity:
+            opnum = op.getopnum()
+            if rop.has_no_side_effect(opnum) and op not in self.longevity:
                 i += 1
                 self.possibly_free_vars_for_op(op)
                 continue
@@ -488,8 +489,7 @@
                 else:
                     self.fprm.temp_boxes.append(box)
             #
-            opnum = op.getopnum()
-            if not we_are_translated() and opnum == -127:
+            if not we_are_translated() and opnum == rop.FORCE_SPILL:
                 self._consider_force_spill(op)
             else:
                 arglocs = prepare_oplist[opnum](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)
+
+ at 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)
+
+ at 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)
+
+ at 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:


More information about the pypy-commit mailing list