Commits

Maciej Fijalkowski committed 8d4029b

(fijal, rguillebert) make resume2 emit RESUME_CLEAR and cleanup resume recording
Additionally write unit tests

Comments (0)

Files changed (17)

rpython/jit/backend/llgraph/runner.py

 
     def process_resume_put(self, op):
         box = op.getarg(0)
+        if isinstance(box, Const):
+            return
         frame_pos = op.getarg(1).getint()
         pos_in_frame = op.getarg(2).getint()
         i = self.framestack[frame_pos].start_pos + pos_in_frame
         self.numbering[box] = i
         self.framestack[frame_pos].registers[pos_in_frame] = box
 
+    def process_resume_clear(self, op):
+        frame_pos = op.getarg(0).getint()
+        frontend_pos = op.getarg(1).getint()
+        self.framestack[frame_pos].registers[frontend_pos] = None
+
     def get_numbering(self, mapping, op):
         lst = []
         for frame in self.framestack:
         for box in frame.force_guard_op.failargs:
             if box is None:
                 value = None
+            elif isinstance(box, Const):
+                xxx
             elif box is not frame.current_op.result:
                 value = frame.env[box]
             else:
         self.framecontent = {}
         i = 0
         for value in newvalues:
-            if value is None:
+            if value is None or isinstance(value, Const):
                 continue
             self.setenv(newargs[i], value)
             i += 1
             arg = self.current_op.failargs[i]
             if arg is None:
                 value = None
+            elif isinstance(arg, Const):
+                value = arg
             else:
                 value = self.env[arg]
             values.append(value)

rpython/jit/backend/llsupport/assembler.py

 from rpython.jit.backend.llsupport.memcpy import memcpy_fn
 from rpython.jit.backend.llsupport.symbolic import WORD
 from rpython.jit.metainterp.history import (INT, REF, FLOAT, JitCellToken,
-    ConstInt, BoxInt, AbstractFailDescr)
+    ConstInt, BoxInt, AbstractFailDescr, Const)
 from rpython.jit.metainterp.resoperation import ResOperation, rop
 from rpython.rlib import rgc
 from rpython.rlib.debug import (debug_start, debug_stop, have_debug_prints,
             inputlocs = loc_positions[i]
             assert len(inputlocs) == len(frame)
             for j, item in enumerate(frame):
-                if item is None:
+                if item is None or isinstance(item, Const):
                     continue
                 pos = inputlocs[j]
                 if pos < GPR_REGS:

rpython/jit/backend/llsupport/test/test_resumebuilder.py

         [i0]
         enter_frame(-1, descr=jitcode)
         resume_put(i0, 0, 2)
+        resume_put(1, 0, 1)
         guard_true(i0)
         leave_frame()
         """, namespace={'jitcode': jitcode})
         looptoken = JitCellToken()
         self.cpu.compile_loop(None, loop.inputargs, loop.operations,
                               looptoken)
-        descr = loop.operations[2].getdescr()
-        assert descr.rd_bytecode_position == 2
+        descr = loop.operations[3].getdescr()
+        assert descr.rd_bytecode_position == 3
         expected_resume = parse("""
         []
         enter_frame(-1, descr=jitcode)
         resume_put(28, 0, 2)
+        resume_put_const(1, 0, 1)
         leave_frame()
         """, namespace={'jitcode': jitcode})
         equaloplists(descr.rd_resume_bytecode.opcodes,

rpython/jit/backend/resumebuilder.py

 
 from rpython.jit.metainterp.resoperation import rop, ResOperation
-from rpython.jit.metainterp.history import ConstInt, Box
+from rpython.jit.metainterp.history import ConstInt, Box, Const
 from rpython.jit.metainterp.resume2 import ResumeBytecode, AbstractResumeReader
 
 class LivenessAnalyzer(AbstractResumeReader):
         self.framestack.append([None] * jitcode.num_regs())
 
     def resume_put(self, box, framepos, frontend_pos):
+        if isinstance(box, Const):
+            return
         self.framestack[framepos][frontend_pos] = box
 
+    def resume_clear(self, framepos, frontend_pos):
+        self.framestack[framepos][frontend_pos] = None
+
+    def resume_put_const(self, box, framepos, frontend_pos):
+        xxx
+
     def resume_new(self, result, descr):
         self.deps[result] = {}
 
         if box in self.deps:
             for dep in self.deps[box].values():
                 self._track(allboxes, dep)
-        allboxes.append(box)
+        if not isinstance(box, Const) and box is not None:
+            allboxes.append(box)
 
     def all_boxes_from(self, frame):
         allboxes = []
         if op.getopnum() == rop.RESUME_PUT:
             box = op.getarg(0)
             args = op.getarglist()
-            if box in self.virtuals:
+            if isinstance(box, Const):
+                newop = op.copy_and_change(rop.RESUME_PUT_CONST)
+            elif box in self.virtuals:
                 newop = op
             else:
                 try:
     count = 0
     for frame in inputframes:
         for x in frame:
-            if x is not None:
+            if x is not None and not isinstance(x, Const):
                 count += 1
     inputargs = [None] * count
     pos = 0
     for frame in inputframes:
         for item in frame:
-            if item is not None:
+            if item is not None and not isinstance(item, Const):
                 inputargs[pos] = item
                 pos += 1
     return inputargs
             framestack = liveness_analyzer.get_live_info()
             for frame in framestack:
                 for item in liveness_analyzer.all_boxes_from(frame):
-                    if item is not None:
-                        last_used[item] = position
-                        frontend_alive[item] = position
+                    last_used[item] = position
+                    frontend_alive[item] = position
 
     for i in range(len(operations)-1, -1, -1):
         op = operations[i]

rpython/jit/codewriter/format.py

             raise AssertionError('\n'.join(msg))
     assert len(asmlines) == len(explines)
 
-def unformat_assembler(text, registers=None):
+def unformat_assembler(text, registers=None, name=None):
     # XXX limited to simple assembler right now
     #
     def unformat_arg(s):
                 extra = []
             insn = [opname] + [unformat_arg(s) for s in words] + extra
             ssarepr.insns.append(tuple(insn))
+    if name is not None:
+        ssarepr.name = name
     return ssarepr
 
 

rpython/jit/metainterp/compile.py

 
     jitcell_token = make_jitcell_token(jitdriver_sd)
     part = create_empty_loop(metainterp)
-    part.inputargs = inputargs[:]
+    part.inputframes = [inputargs[:]]
     h_ops = history.operations
     part.resume_at_jump_descr = resume_at_jump_descr
     part.operations = [ResOperation(rop.LABEL, inputargs, None, descr=TargetToken(jitcell_token))] + \
     all_target_tokens = [target_token]
 
     loop = create_empty_loop(metainterp)
-    loop.inputargs = part.inputargs
+    loop.inputframes = part.inputframes
     loop.operations = part.operations
     loop.quasi_immutable_deps = {}
     if part.quasi_immutable_deps:
 
     if not loop.quasi_immutable_deps:
         loop.quasi_immutable_deps = None
-    for box in loop.inputargs:
-        assert isinstance(box, Box)
+    for frame in loop.inputframes:
+        for box in frame:
+            assert isinstance(box, Box)
 
     loop.original_jitcell_token = jitcell_token
     for label in all_target_tokens:
     assert partial_trace.operations[-1].getopnum() == rop.LABEL
 
     part = create_empty_loop(metainterp)
-    part.inputargs = inputargs[:]
+    part.inputframes = [inputargs[:]]
     part.resume_at_jump_descr = resume_at_jump_descr
     h_ops = history.operations
 
     if quasi_immutable_deps:
         loop.quasi_immutable_deps = quasi_immutable_deps
 
-    for box in loop.inputargs:
-        assert isinstance(box, Box)
+    for frame in loop.inputframes:
+        for box in frame:
+            assert isinstance(box, Box)
 
     target_token = loop.operations[-1].getdescr()
     resumekey.compile_and_attach(metainterp, loop)
 def patch_new_loop_to_load_virtualizable_fields(loop, jitdriver_sd):
     vinfo = jitdriver_sd.virtualizable_info
     extra_ops = []
-    inputargs = loop.inputargs
+    inputargs = loop.inputframes[0]
     vable_box = inputargs[jitdriver_sd.index_of_virtualizable]
     i = jitdriver_sd.num_red_args
-    loop.inputargs = inputargs[:i]
+    loop.inputframes = [inputargs[:i]]
     for descr in vinfo.static_field_descrs:
         assert i < len(inputargs)
         box = inputargs[i]
     metainterp_sd.profiler.start_backend()
     debug_start("jit-backend")
     try:
-        asminfo = do_compile_loop(metainterp_sd, loop.inputargs,
+        assert len(loop.inputframes) == 1
+        asminfo = do_compile_loop(metainterp_sd, loop.inputframes[0],
                                   operations, original_jitcell_token,
                                   name=loopname)
     finally:
         ops_offset = asminfo.ops_offset
     else:
         ops_offset = None
-    metainterp_sd.logger_ops.log_loop(loop.inputargs, loop.operations, n,
+    metainterp_sd.logger_ops.log_loop(loop.inputframes[0], loop.operations, n,
                                       type, ops_offset,
                                       name=loopname)
     #
     # it does not work -- i.e. none of the existing old_loop_tokens match.
     new_trace = create_empty_loop(metainterp)
     new_trace.inputframes = metainterp.history.inputframes[:]
-    new_trace.inputlocs = metainterp.history.inputlocs[:]
+    if metainterp.history.inputlocs is not None:
+        new_trace.inputlocs = metainterp.history.inputlocs[:]
     # clone ops, as optimize_bridge can mutate the ops
 
     new_trace.operations = [op.clone() for op in metainterp.history.operations]

rpython/jit/metainterp/history.py

         return 'TargetToken(%d)' % compute_unique_id(self)
 
 class TreeLoop(object):
-    inputargs = None
+    inputframes = None
+    inputlocs = None
     operations = None
     call_pure_results = None
     logops = None
         return self.operations
 
     def get_display_text(self):    # for graphpage.py
-        return self.name + '\n' + repr(self.inputargs)
+        return self.name + '\n' + repr(self.inputframes)
 
     def show(self, errmsg=None):
         "NOT_RPYTHON"
 
     def check_consistency(self):     # for testing
         "NOT_RPYTHON"
-        self.check_consistency_of(self.inputargs, self.operations)
+        self.check_consistency_of(self.inputframes, self.operations)
         for op in self.operations:
             descr = op.getdescr()
             if op.getopnum() == rop.LABEL and isinstance(descr, TargetToken):
 
     @staticmethod
     def check_consistency_of(inputargs, operations):
-        for box in inputargs:
-            assert isinstance(box, Box), "Loop.inputargs contains %r" % (box,)
-        seen = dict.fromkeys(inputargs)
-        assert len(seen) == len(inputargs), (
+        seen = {}
+        all = 0
+        for frame in inputargs:
+            for box in frame:
+                assert isinstance(box, Box), "Loop.inputargs contains %r" % (box,)
+                seen[box] = None
+                all += 1
+        assert len(seen) == all, (
                "duplicate Box in the Loop.inputargs")
         TreeLoop.check_consistency_of_branch(operations, seen)
 
 
     def dump(self):
         # RPython-friendly
-        print '%r: inputargs =' % self, self._dump_args(self.inputargs)
+        print '%r: inputargs =' % self, self._dump_args(self.inputframes)
         for op in self.operations:
             args = op.getarglist()
             print '\t', op.getopname(), self._dump_args(args), \
     if omit_finish and operations[-1].getopnum() == rop.FINISH:
         # xxx obscure
         return
-    result.extend(operations)
+    result.extend([op for op in operations if not op.is_resume()])
     for op in operations:
         if op.is_guard() and op.getdescr():
             if hasattr(op.getdescr(), '_debug_suboperations'):
 
 class History(object):
     def __init__(self):
-        self.inputargs = None
+        self.inputframes = None
+        self.inputlocs = None
         self.operations = []
 
     def record(self, opnum, argboxes, resbox, descr=None):

rpython/jit/metainterp/jitdriver.py

 class JitDriverStaticData(object):
     """There is one instance of this class per JitDriver used in the program.
     """
+    virtualizable_info = None
+    greenfield_info = None
+    
+    def __init__(self, jitdriver, portal_graph, result_type):
+        self.jitdriver = jitdriver
+        self.portal_graph = portal_graph
+        self.result_type = result_type
+        self.num_green_args = len(jitdriver.greens)
+        self.num_red_args = len(jitdriver.reds)
+    
     # This is just a container with the following attributes (... set by):
-    #    self.jitdriver         ... rpython.jit.metainterp.warmspot
-    #    self.portal_graph      ... rpython.jit.metainterp.warmspot
     #    self.portal_runner_ptr ... rpython.jit.metainterp.warmspot
     #    self.portal_runner_adr ... rpython.jit.metainterp.warmspot
     #    self.portal_calldescr  ... rpython.jit.metainterp.warmspot
-    #    self.num_green_args    ... rpython.jit.metainterp.warmspot
-    #    self.num_red_args      ... rpython.jit.metainterp.warmspot
     #    self.red_args_types    ... rpython.jit.metainterp.warmspot
-    #    self.result_type       ... rpython.jit.metainterp.warmspot
     #    self.virtualizable_info... rpython.jit.metainterp.warmspot
     #    self.greenfield_info   ... rpython.jit.metainterp.warmspot
     #    self.warmstate         ... rpython.jit.metainterp.warmspot

rpython/jit/metainterp/optimizeopt/__init__.py

 def optimize_trace(metainterp_sd, loop, enable_opts, inline_short_preamble=True):
     """Optimize loop.operations to remove internal overheadish operations.
     """
+    from rpython.jit.backend.resumebuilder import flatten
 
     debug_start("jit-optimize")
     try:
-        loop.logops = metainterp_sd.logger_noopt.log_loop(loop.inputargs,
-                                                          loop.operations)
+        loop.logops = metainterp_sd.logger_noopt.log_loop(
+            flatten(loop.inputframes),
+            loop.operations)
         optimizations, unroll = build_opt_chain(metainterp_sd, enable_opts)
         if unroll:
             optimize_unroll(metainterp_sd, loop, optimizations, inline_short_preamble)

rpython/jit/metainterp/optimizeopt/util.py

 
 # ____________________________________________________________
 
-def equaloplists(oplist1, oplist2, remap={}, text_right=None):
+def equaloplists(oplist1, oplist2, remap=None, text_right=None,
+                 cache=True):
     # try to use the full width of the terminal to display the list
     # unfortunately, does not work with the default capture method of py.test
     # (which is fd), you you need to use either -s or --capture=sys, else you
     # get the standard 80 columns width
+    if remap is None:
+        remap = {}
     totwidth = py.io.get_terminal_width()
     width = totwidth / 2 - 1
     print ' Comparing lists '.center(totwidth, '-')
         for i in range(op1.numargs()):
             x = op1.getarg(i)
             y = op2.getarg(i)
+            if cache and y not in remap:
+                remap[y] = x
             assert x.same_box(remap.get(y, y))
         if op2.result in remap:
             if op2.result is None:
                 assert op1.result == remap[op2.result]
             else:
+                if cache and op2.result not in remap:
+                    remap[op2.result] = op1.result
                 assert op1.result.same_box(remap[op2.result])
         else:
             remap[op2.result] = op1.result

rpython/jit/metainterp/pyjitpl.py

 from rpython.jit.metainterp.logger import Logger
 from rpython.jit.metainterp.optimizeopt.util import args_dict_box
 from rpython.jit.metainterp.resoperation import rop
+from rpython.jit.metainterp.resume2 import ResumeRecorder
 from rpython.rlib import nonconst, rstack
 from rpython.rlib.debug import debug_start, debug_stop, debug_print, make_sure_not_resized
 from rpython.rlib.jit import Counters
     def get_current_position_info(self):
         return self.jitcode.get_live_vars_info(self.pc)
 
-    def get_list_of_active_boxes(self, in_a_call):
-        if in_a_call:
-            # If we are not the topmost frame, self._result_argcode contains
-            # the type of the result of the call instruction in the bytecode.
-            # We use it to clear the box that will hold the result: this box
-            # is not defined yet.
-            argcode = self._result_argcode
-            index = ord(self.bytecode[self.pc - 1])
-            if   argcode == 'i': self.registers_i[index] = history.CONST_FALSE
-            elif argcode == 'r': self.registers_r[index] = history.CONST_NULL
-            elif argcode == 'f': self.registers_f[index] = history.CONST_FZERO
-            self._result_argcode = '?'     # done
-        #
-        info = self.get_current_position_info()
-        start_i = 0
-        start_r = start_i + info.get_register_count_i()
-        start_f = start_r + info.get_register_count_r()
-        total   = start_f + info.get_register_count_f()
-        # allocate a list of the correct size
-        env = [None] * total
-        make_sure_not_resized(env)
-        # fill it now
-        for i in range(info.get_register_count_i()):
-            index = info.get_register_index_i(i)
-            env[start_i + i] = self.registers_i[index]
-        for i in range(info.get_register_count_r()):
-            index = info.get_register_index_r(i)
-            env[start_r + i] = self.registers_r[index]
-        for i in range(info.get_register_count_f()):
-            index = info.get_register_index_f(i)
-            env[start_f + i] = self.registers_f[index]
-        return env
-
     def replace_active_box_in_frame(self, oldbox, newbox):
         if isinstance(oldbox, history.BoxInt):
             count = self.jitcode.num_regs_i()
         # but we should not follow calls to that graph
         return self.do_residual_call(funcbox, argboxes, calldescr, pc)
 
-    def emit_resume_data(self, pos):
+    def emit_resume_data(self, pos, in_call):
         i = 0
         history = self.metainterp.history
+        boxes = self.get_list_of_active_boxes(in_call)
+        #xxx
+        #xxx
         for i in range(self.jitcode.num_regs_i()):
             box = self.registers_i[i]
-            if box is not None and box not in self.resume_cache:
+            if box is not None and (box, pos, i) not in self.resume_cache:
                 history.record(rop.RESUME_PUT,
                                [box, ConstInt(pos), ConstInt(i)], None)
-                self.resume_cache[box] = None
+                self.resume_cache[(box, pos, i)] = None
         start = self.jitcode.num_regs_i()
         for i in range(self.jitcode.num_regs_r()):
             box = self.registers_r[i]
-            if box is not None and box not in self.resume_cache:
+            if box is not None and (box, pos, i) not in self.resume_cache:
                 history.record(rop.RESUME_PUT,
                                [box, ConstInt(pos), ConstInt(i + start)], None)
-                self.resume_cache[box] = None
+                self.resume_cache[(box, pos, i)] = None
         start = self.jitcode.num_regs_i() + self.jitcode.num_regs_r()
         for i in range(self.jitcode.num_regs_f()):
             box = self.registers_f[i]
-            if box is not None and box not in self.resume_cache:
+            if box is not None and (box, pos, i) not in self.resume_cache:
                 history.record(rop.RESUME_PUT,
                                [box, ConstInt(pos), ConstInt(i + start)], None)
-                self.resume_cache[box] = None
+                self.resume_cache[(box, pos, i)] = None
         history.record(rop.RESUME_SET_PC, [ConstInt(self.pc)], None)
 
 # ____________________________________________________________
         self.retracing_from = -1
         self.call_pure_results = args_dict_box()
         self.heapcache = HeapCache()
+        self.resumerecorder = ResumeRecorder(self)
 
         self.call_ids = []
         self.current_call_id = 0
         else:
             pc = -1
         if record_resume:
-            self.history.record(rop.ENTER_FRAME, [ConstInt(pc)], None,
-                                descr=jitcode)
+            self.resumerecorder.enter_frame(pc, jitcode)
         if jitcode.is_portal:
             self.portal_call_depth += 1
             self.call_ids.append(self.current_call_id)
         return f
 
     def popframe(self):
-        self.history.record(rop.LEAVE_FRAME, [], None)
+        self.resumerecorder.leave_frame()
         frame = self.framestack.pop()
         jitcode = frame.jitcode
         if jitcode.is_portal:
         else:
             resumedescr = compile.ResumeGuardDescr()
             resumedescr.guard_opnum = opnum # XXX kill me
-        self.sync_resume_data(resumedescr, resumepc)
+        self.resumerecorder.resume_point(resumedescr, resumepc)
         guard_op = self.history.record(opnum, moreargs, None,
                                              descr=resumedescr)
         self.staticdata.profiler.count_ops(opnum, Counters.GUARDS)
         # count
         self.attach_debug_info(guard_op)
         return guard_op
-    
-    def sync_resume_data(self, resumedescr, resumepc):
-        for i, frame in enumerate(self.framestack):
-            frame.emit_resume_data(i)
-    
+        
     def capture_resumedata(self, resumedescr, resumepc=-1):
         XXXX
         virtualizable_boxes = None
         num_green_args = self.jitdriver_sd.num_green_args
         original_greenkey = original_boxes[:num_green_args]
         self.resumekey = compile.ResumeFromInterpDescr(original_greenkey)
-        self.history.inputargs = original_boxes[num_green_args:]
+        self.history.inputframes = [original_boxes[num_green_args:]]
         self.seen_loop_header_for_jdindex = -1
         try:
             self.interpret()
         return ints[:], refs[:], floats[:]
 
     def raise_continue_running_normally(self, live_arg_boxes, loop_token):
-        self.history.inputargs = None
+        self.history.inputframes = None
+        self.history.inputlocs = None
         self.history.operations = None
         # For simplicity, we just raise ContinueRunningNormally here and
         # ignore the loop_token passed in.  It means that we go back to

rpython/jit/metainterp/resoperation.py

     'RESUME_PUT/3', # arguments are as follows - box or position in the backend,
                     # the frame index (counting from top) and position in the
                     # frontend
+    'RESUME_PUT_CONST/3', # the same but for a constant
     'RESUME_NEW/0d',
     'RESUME_SETFIELD_GC/2d',
     'RESUME_SET_PC/1',
+    'RESUME_CLEAR/2',
     '_RESUME_LAST', # ----- end of resume only operations ------
     '_NOSIDEEFFECT_LAST', # ----- end of no_side_effect operations -----
 

rpython/jit/metainterp/resume2.py

 
 from rpython.jit.metainterp.resoperation import rop
-from rpython.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat
+from rpython.jit.metainterp.history import BoxInt, BoxPtr, BoxFloat, ConstInt
+from rpython.jit.metainterp import history
 from rpython.jit.codewriter.jitcode import JitCode
 from rpython.rlib import rstack
 
         self.pc = -1
         
 class AbstractResumeReader(object):
+    """ A resume reader that can follow resume until given point. Consult
+    the concrete classes for details
+    """
+    
     def __init__(self):
         self.framestack = []
+        self.consts = [] # XXX cache?
 
     def rebuild(self, faildescr):
         self._rebuild_until(faildescr.rd_resume_bytecode,
         jitframe_pos = jitframe_pos_const.getint()
         self.framestack[frame_no].registers[frontend_position] = jitframe_pos
 
+    def resume_clear(self, frame_no, frontend_position):
+        self.framestack[frame_no].registers[frontend_position] = -1
+
+    def resume_put_const(self, const, frame_no, frontend_position):
+        self.framestack[frame_no].registers[frontend_position] = - 2 - len(self.consts)
+        self.consts.append(const)
+
     def resume_set_pc(self, pc):
         self.framestack[-1].pc = pc
 
             elif op.getopnum() == rop.RESUME_PUT:
                 self.resume_put(op.getarg(0), op.getarg(1).getint(),
                                  op.getarg(2).getint())
+            elif op.getopnum() == rop.RESUME_PUT_CONST:
+                self.resume_put_const(op.getarg(0), op.getarg(1).getint(),
+                                      op.getarg(2).getint())
             elif op.getopnum() == rop.RESUME_NEW:
                 self.resume_new(op.result, op.getdescr())
             elif op.getopnum() == rop.RESUME_SETFIELD_GC:
                                         op.getdescr())
             elif op.getopnum() == rop.RESUME_SET_PC:
                 self.resume_set_pc(op.getarg(0).getint())
+            elif op.getopnum() == rop.RESUME_CLEAR:
+                self.resume_clear(op.getarg(0).getint(),
+                                  op.getarg(1).getint())
             elif not op.is_resume():
                 pos += 1
                 continue
         return self.metainterp.cpu.get_int_value(self.deadframe, jitframe_pos)
 
 class DirectResumeReader(AbstractResumeReader):
+    """ Directly read values from the jitframe and put them in the blackhole
+    interpreter
+    """
+    
     def __init__(self, binterpbuilder, cpu, deadframe):
         self.bhinterpbuilder = binterpbuilder
         self.cpu = cpu
             curbh.setposition(jitcode, frame.pc)
             pos = 0
             for i in range(jitcode.num_regs_i()):
-                jitframe_pos = frame.registers[pos]
-                if jitframe_pos != -1:
-                    curbh.registers_i[i] = self.cpu.get_int_value(
-                        self.deadframe, jitframe_pos)
+                self.store_int_value(curbh, i, frame.registers[pos])
                 pos += 1
             for i in range(jitcode.num_regs_r()):
-                jitframe_pos = frame.registers[pos]
-                if jitframe_pos != -1:
-                    curbh.registers_r[i] = self.cpu.get_ref_value(
-                        self.deadframe, jitframe_pos)
+                self.store_ref_value(curbh, i, frame.registers[pos])
                 pos += 1
             for i in range(jitcode.num_regs_f()):
-                jitframe_pos = frame.registers[pos]
-                if jitframe_pos != -1:
-                    curbh.registers_f[i] = self.cpu.get_float_value(
-                        self.deadframe, jitframe_pos)
+                self.store_float_value(curbh, i, frame.registers[pos])
                 pos += 1
         return curbh
 
+    def store_int_value(self, curbh, i, jitframe_pos):
+        if jitframe_pos >= 0:
+            curbh.registers_i[i] = self.cpu.get_int_value(
+                self.deadframe, jitframe_pos)
+        elif jitframe_pos < -1:
+            curbh.registers_i[i] = self.consts[-jitframe_pos - 2].getint()
+
+    def store_ref_value(self, curbh, i, jitframe_pos):
+        if jitframe_pos >= 0:
+            curbh.registers_r[i] = self.cpu.get_ref_value(
+                self.deadframe, jitframe_pos)
+        elif jitframe_pos < -1:
+            curbh.registers_r[i] = self.consts[-jitframe_pos - 2].getref_base()
+
+    def store_float_value(self, curbh, i, jitframe_pos):
+        if jitframe_pos >= 0:
+            curbh.registers_f[i] = self.cpu.get_float_value(
+                self.deadframe, jitframe_pos)
+        elif jitframe_pos < -1:
+            curbh.registers_f[i] = self.consts[-jitframe_pos - 2].getfloat()
+
 class BoxResumeReader(AbstractResumeReader):
+    """ Create boxes corresponding to the resume and store them in
+    the metainterp
+    """
+    
     def __init__(self, metainterp, deadframe):
         self.metainterp = metainterp
         self.deadframe = deadframe
         AbstractResumeReader.__init__(self)
 
-    def get_int_box(self, pos):
-        return BoxInt(self.metainterp.cpu.get_int_value(self.deadframe, pos))
+    def store_int_box(self, res, pos, miframe, i, jitframe_pos):
+        if jitframe_pos == -1:
+            return
+        if jitframe_pos >= 0:
+            box = BoxInt(self.metainterp.cpu.get_int_value(self.deadframe,
+                                                           jitframe_pos))
+        elif jitframe_pos <= -2:
+            box = self.consts[-jitframe_pos - 2]
+        miframe.registers_i[i] = box
+        res[-1][pos] = box
 
-    def get_ref_box(self, pos):
-        return BoxPtr(self.metainterp.cpu.get_ref_value(self.deadframe, pos))
+    def store_ref_box(self, res, pos, miframe, i, jitframe_pos):
+        if jitframe_pos == -1:
+            return
+        if jitframe_pos >= 0:
+            box = BoxPtr(self.metainterp.cpu.get_ref_value(self.deadframe,
+                                                           jitframe_pos))
+        elif jitframe_pos <= -2:
+            box = self.consts[-jitframe_pos - 2]
+        miframe.registers_r[i] = box
+        res[-1][pos] = box
 
-    def get_float_box(self, pos):
-        return BoxFloat(self.metainterp.cpu.get_float_value(self.deadframe,
-                                                            pos))
+    def store_float_box(self, res, pos, miframe, i, jitframe_pos):
+        if jitframe_pos == -1:
+            return
+        if jitframe_pos >= 0:
+            box = BoxFloat(self.metainterp.cpu.get_float_value(self.deadframe,
+                                                             jitframe_pos))
+        elif jitframe_pos <= -2:
+            box = self.consts[-jitframe_pos - 2]
+        miframe.registers_f[i] = box
+        res[-1][pos] = box
 
     def finish(self):
         res = []
             miframe.pc = frame.pc
             pos = 0
             for i in range(jitcode.num_regs_i()):
-                jitframe_pos = frame.registers[pos]
-                if jitframe_pos != -1:
-                    box = self.get_int_box(jitframe_pos)
-                    miframe.registers_i[i] = box
-                    res[-1][pos] = box
+                self.store_int_box(res, pos, miframe, i, frame.registers[pos])
                 pos += 1
             for i in range(jitcode.num_regs_r()):
-                jitframe_pos = frame.registers[pos]
-                if jitframe_pos != -1:
-                    box = self.get_int_box(jitframe_pos)
-                    res[-1][pos] = box
-                    miframe.registers_r[i] = box
+                self.store_ref_box(res, pos, miframe, i, frame.registers[pos])
                 pos += 1
             for i in range(jitcode.num_regs_f()):
-                jitframe_pos = frame.registers[pos]
-                if jitframe_pos != -1:
-                    box = self.get_int_box(jitframe_pos)
-                    res[-1][pos] = box
-                    miframe.registers_f[i] = box
+                self.store_float_box(res, pos, miframe, i, frame.registers[pos])
                 pos += 1
         return res, [f.registers for f in self.framestack]
             
 def rebuild_from_resumedata(metainterp, deadframe, faildescr):
+    """ Reconstruct metainterp frames from the resumedata
+    """
     return BoxResumeReader(metainterp, deadframe).rebuild(faildescr)
 
 def blackhole_from_resumedata(interpbuilder, metainterp_sd, faildescr,
                               deadframe, all_virtuals=None):
+    """ Reconstruct the blackhole interpreter from the resume data
+    """
     assert all_virtuals is None
     #rstack._stack_criticalcode_start()
     #try:
                                        deadframe).rebuild(faildescr)
     
     return last_bhinterp
+
+class ResumeRecorder(object):
+    """ Created by metainterp to record the resume as we record operations
+    """
+    def __init__(self, metainterp):
+        self.metainterp = metainterp
+        self.cachestack = []
+
+    def enter_frame(self, pc, jitcode):
+        self.metainterp.history.record(rop.ENTER_FRAME, [ConstInt(pc)], None,
+                                       descr=jitcode)
+        self.cachestack.append([None] * jitcode.num_regs())
+
+    def leave_frame(self):
+        self.metainterp.history.record(rop.LEAVE_FRAME, [], None)
+        self.cachestack.pop()
+
+    def resume_point(self, resumedescr, resumepc):
+        framestack = self.metainterp.framestack
+        for i, frame in enumerate(framestack):
+            self._emit_resume_data(resumepc, frame, i, not i == len(framestack))
+
+    def process_box(self, index_in_frontend, frame_pos, box):
+        cache = self.cachestack[frame_pos]
+        self.marked[index_in_frontend] = box
+        if cache[index_in_frontend] is box:
+            return
+        cache[index_in_frontend] = box
+        self.metainterp.history.record(rop.RESUME_PUT,
+                                       [box, ConstInt(frame_pos),
+                                        ConstInt(index_in_frontend)], None)
+
+    def _emit_resume_data(self, resume_pc, frame, frame_pos, in_a_call):
+        self.marked = [None] * len(self.cachestack[frame_pos])
+        if in_a_call:
+            # If we are not the topmost frame, frame._result_argcode contains
+            # the type of the result of the call instruction in the bytecode.
+            # We use it to clear the box that will hold the result: this box
+            # is not defined yet.
+            argcode = frame._result_argcode
+            index = ord(frame.bytecode[frame.pc - 1])
+            if   argcode == 'i': frame.registers_i[index] = history.CONST_FALSE
+            elif argcode == 'r': frame.registers_r[index] = history.CONST_NULL
+            elif argcode == 'f': frame.registers_f[index] = history.CONST_FZERO
+            frame._result_argcode = '?'     # done
+        #
+        info = frame.get_current_position_info()
+        start_i = 0
+        start_r = start_i + info.get_register_count_i()
+        start_f = start_r + info.get_register_count_r()
+        # fill it now
+        for i in range(info.get_register_count_i()):
+            index = info.get_register_index_i(i)
+            self.process_box(index, frame_pos, frame.registers_i[index])
+        for i in range(info.get_register_count_r()):
+            index = info.get_register_index_r(i)
+            self.process_box(index + start_r, frame_pos,
+                             frame.registers_i[index])
+        for i in range(info.get_register_count_f()):
+            index = info.get_register_index_f(i)
+            self.process_box(index + start_f, frame_pos,
+                             frame.registers_i[index])
+
+        history = self.metainterp.history
+        cache = self.cachestack[frame_pos]
+        for i in range(len(self.marked)):
+            if self.marked[i] is None and cache[i] is not None:
+                cache[i] = None
+                history.record(rop.RESUME_CLEAR, [ConstInt(frame_pos),
+                                                  ConstInt(i)], None)
+        history.record(rop.RESUME_SET_PC, [ConstInt(resume_pc)], None)
+        self.marked = None

rpython/jit/metainterp/test/support.py

     def meta_interp(self, *args, **kwds):
         kwds['CPUClass'] = self.CPUClass
         kwds['type_system'] = self.type_system
+        kwds['enable_opts'] = ''
         if "backendopt" not in kwds:
             kwds["backendopt"] = False
         old = codewriter.CodeWriter.debug

rpython/jit/metainterp/test/test_loop.py

             found = 0
             for op in get_stats().loops[0]._all_operations():
                 if op.getopname() == 'guard_true':
-                    liveboxes = op.getfailargs()
-                    assert len(liveboxes) == 2     # x, y (in some order)
-                    assert isinstance(liveboxes[0], history.BoxInt)
-                    assert isinstance(liveboxes[1], history.BoxInt)
                     found += 1
             if 'unroll' in self.enable_opts:
                 assert found == 2

rpython/jit/metainterp/test/test_resume2.py

 import py
 from rpython.jit.tool.oparser import parse
 from rpython.jit.codewriter.jitcode import JitCode
-from rpython.jit.metainterp.history import AbstractDescr
+from rpython.jit.metainterp.history import AbstractDescr, Const, INT, Stats
 from rpython.jit.metainterp.resume2 import rebuild_from_resumedata,\
      ResumeBytecode, AbstractResumeReader
+from rpython.jit.codewriter.format import unformat_assembler
+from rpython.jit.codewriter.codewriter import CodeWriter
+from rpython.jit.backend.llgraph.runner import LLGraphCPU
+from rpython.jit.metainterp.pyjitpl import MetaInterp, MetaInterpStaticData
+from rpython.jit.metainterp.jitdriver import JitDriverStaticData
+from rpython.jit.metainterp.warmstate import JitCell
+from rpython.jit.metainterp.jitexc import DoneWithThisFrameInt
+from rpython.jit.metainterp.optimizeopt.util import equaloplists
+from rpython.rlib.jit import JitDriver
 
 
 class Descr(AbstractDescr):
         []
         enter_frame(-1, descr=jitcode1)
         resume_put(10, 0, 1)
+        resume_put_const(1, 0, 2)
         leave_frame()
-        """, namespace={'jitcode1': jitcode})
+        """, namespace= {'jitcode1': jitcode})
         descr = Descr()
         descr.rd_resume_bytecode = ResumeBytecode(resume_loop.operations)
-        descr.rd_bytecode_position = 2
+        descr.rd_bytecode_position = 3
         metainterp = MockMetaInterp()
         metainterp.cpu = MockCPU()
         rebuild_from_resumedata(metainterp, "myframe", descr)
         assert len(metainterp.framestack) == 1
         f = metainterp.framestack[-1]
         assert f.registers_i[1].getint() == 13
+        assert isinstance(f.registers_i[2], Const)
+        assert f.registers_i[2].getint() == 1
 
     def test_nested_call(self):
         jitcode1 = JitCode("jitcode")
         descr = Descr()
         descr.rd_resume_bytecode = ResumeBytecode(resume_loop.operations)
         descr.rd_bytecode_position = 5
-        state = rebuild_from_resumedata(metainterp, "myframe", descr)
+        rebuild_from_resumedata(metainterp, "myframe", descr)
         assert len(metainterp.framestack) == 2
         f = metainterp.framestack[-1]
         f2 = metainterp.framestack[0]
         locs = rebuild_locs_from_resumedata(descr)
         assert locs == [[8, 11], [12]]
 
-    def test_resume_put_const(self):
-        xxx
+class AssemblerExecuted(Exception):
+    pass
+
+class FakeWarmstate(object):
+    enable_opts = []
+    
+    def __init__(self):
+        self.jitcell = JitCell()
+    
+    def get_location_str(self, greenkey):
+        return "foo"
+
+    def jit_cell_at_key(self, greenkey):
+        return self.jitcell
+
+    def attach_procedure_to_interp(self, *args):
+        pass
+
+    def execute_assembler(self, token, *args):
+        raise AssemblerExecuted(*args)
+
+def get_metainterp(assembler, no_reds=0):
+    codewriter = CodeWriter()
+    ssarepr = unformat_assembler(assembler, name='one')
+    jitcode = codewriter.assembler.assemble(ssarepr)
+    jitcode.is_portal = True
+    reds = ['v' + str(i) for i in range(no_reds)]
+    jitdriver_sd = JitDriverStaticData(JitDriver(greens = [],
+                                                 reds = reds),
+                                       None, INT)
+    jitdriver_sd.mainjitcode = jitcode
+    jitdriver_sd.warmstate = FakeWarmstate()
+    jitdriver_sd.no_loop_header = False
+    jitdriver_sd._get_printable_location_ptr = None
+    codewriter.setup_jitdriver(jitdriver_sd)
+    stats = Stats()
+    cpu = LLGraphCPU(None, stats)
+    metainterp_sd = MetaInterpStaticData(cpu, None)
+    metainterp_sd.finish_setup(codewriter)
+    return MetaInterp(metainterp_sd, jitdriver_sd), stats, jitdriver_sd
+    
+class TestResumeRecorder(object):
+    def test_simple(self):
+        assembler = """
+        L1:
+        -live- %i0, %i1, %i2
+        jit_merge_point $0, I[], R[], F[], I[%i0, %i1, %i2], R[], F[]
+        -live- %i0, %i1, %i2
+        int_add %i2, %i0 -> %i2
+        int_sub %i1, $1 -> %i1
+        goto_if_not_int_gt %i1, $0, L2
+        -live- %i0, %i1, %i2, L2
+        loop_header $0
+        goto L1
+        ---
+        L2:
+        int_mul %i2, $2 -> %i0
+        int_return %i0
+        """
+        metainterp, stats, jitdriver_sd = get_metainterp(assembler, no_reds=3)
+        jitcode = jitdriver_sd.mainjitcode
+        try:
+            metainterp.compile_and_run_once(jitdriver_sd, 6, 7, 0)
+        except AssemblerExecuted, e:
+            assert e.args == (6, 6, 6)
+        else:
+            raise Exception("did not exit")
+        resume_ops = [o for o in stats.operations if o.is_resume()]
+        expected = parse("""
+        [i0, i1, i2]
+        enter_frame(-1, descr=jitcode)
+        resume_put(i0, 0, 0)
+        resume_put(i1, 0, 1)
+        resume_put(i2, 0, 2)
+        resume_set_pc(24)
+        """, namespace={'jitcode': jitcode})
+        equaloplists(resume_ops, expected.operations, cache=True)
+
+    def test_live_boxes(self):
+        assembler = """
+        L1:
+        -live- %i0, %i1, %i2
+        jit_merge_point $0, I[], R[], F[], I[%i0, %i1, %i2], R[], F[]
+        -live- %i0, %i1, %i2
+        goto_if_not_int_gt %i1, $0, L2
+        -live- %i0, %i1, L2
+        loop_header $0
+        goto L1
+        ---
+        L2:
+        int_return %i0
+        """
+        metainterp, stats, jitdriver_sd = get_metainterp(assembler, no_reds=3)
+        jitcode = jitdriver_sd.mainjitcode
+        try:
+            metainterp.compile_and_run_once(jitdriver_sd, -1, -1, 0)
+        except DoneWithThisFrameInt:
+            pass
+        resume_ops = [o for o in stats.operations if o.is_resume()]
+        expected = parse("""
+        [i0, i1, i2]
+        enter_frame(-1, descr=jitcode)
+        resume_put(i0, 0, 0)
+        resume_put(i1, 0, 1)
+        resume_set_pc(16)
+        leave_frame()
+        """, namespace={'jitcode': jitcode})
+        equaloplists(resume_ops, expected.operations, cache=True)
+
+    def test_live_boxes_2(self):
+        assembler = """
+        L1:
+        -live- %i0, %i1, %i2
+        jit_merge_point $0, I[], R[], F[], I[%i0, %i1, %i2], R[], F[]
+        -live- %i0, %i1, %i2
+        goto_if_not_int_gt %i1, $0, L2
+        -live- %i0, %i1, %i2, L2
+        goto_if_not_int_gt %i2, $0, L2
+        -live- %i0, %i2, L2
+        loop_header $0
+        goto L1
+        ---
+        L2:
+        int_return %i0
+        """
+        metainterp, stats, jitdriver_sd = get_metainterp(assembler, no_reds=3)
+        jitcode = jitdriver_sd.mainjitcode
+        try:
+            metainterp.compile_and_run_once(jitdriver_sd, -1, 13, -1)
+        except DoneWithThisFrameInt:
+            pass
+        resume_ops = [o for o in stats.operations if o.is_resume()]
+        expected = parse("""
+        [i0, i1, i2]
+        enter_frame(-1, descr=jitcode)
+        resume_put(i0, 0, 0)
+        resume_put(i1, 0, 1)
+        resume_put(i2, 0, 2)
+        resume_set_pc(-1)
+        resume_clear(0, 1)
+        resume_set_pc(-1)
+        leave_frame()
+        """, namespace={'jitcode': jitcode})
+        equaloplists(resume_ops, expected.operations, cache=True)

rpython/jit/metainterp/warmspot.py

 
     def split_graph_and_record_jitdriver(self, graph, block, pos):
         op = block.operations[pos]
-        jd = JitDriverStaticData()
-        jd._jit_merge_point_in = graph
         args = op.args[2:]
         s_binding = self.translator.annotator.binding
-        jd._portal_args_s = [s_binding(v) for v in args]
         graph = copygraph(graph)
         [jmpp] = find_jit_merge_points([graph])
         graph.startblock = support.split_before_jit_merge_point(*jmpp)
             assert isinstance(v, Variable)
         assert len(dict.fromkeys(graph.getargs())) == len(graph.getargs())
         self.translator.graphs.append(graph)
-        jd.portal_graph = graph
         # it's a bit unbelievable to have a portal without func
         assert hasattr(graph, "func")
         graph.func._dont_inline_ = True
         graph.func._jit_unroll_safe_ = True
-        jd.jitdriver = block.operations[pos].args[1].value
+        result_type = history.getkind(graph.getreturnvar().concretetype)[0]
+        jd = JitDriverStaticData(block.operations[pos].args[1].value, graph,
+                                 result_type)
+        jd._portal_args_s = [s_binding(v) for v in args]
+        jd._jit_merge_point_in = graph
         jd.portal_runner_ptr = "<not set so far>"
-        jd.result_type = history.getkind(jd.portal_graph.getreturnvar()
-                                         .concretetype)[0]
         self.jitdrivers_sd.append(jd)
 
     def check_access_directly_sanity(self, graphs):
         ALLARGS = [v.concretetype for v in (greens_v + reds_v)]
         jd._green_args_spec = [v.concretetype for v in greens_v]
         jd.red_args_types = [history.getkind(v.concretetype) for v in reds_v]
-        jd.num_green_args = len(jd._green_args_spec)
-        jd.num_red_args = len(jd.red_args_types)
         RESTYPE = graph.getreturnvar().concretetype
         (jd._JIT_ENTER_FUNCTYPE,
          jd._PTR_JIT_ENTER_FUNCTYPE) = self.cpu.ts.get_FuncType(ALLARGS, lltype.Void)