Author: Maciej Fijalkowski <fij...@gmail.com> Branch: resume-refactor Changeset: r68666:8d4029b5e97f Date: 2014-01-14 12:16 +0100 http://bitbucket.org/pypy/pypy/changeset/8d4029b5e97f/
Log: (fijal, rguillebert) make resume2 emit RESUME_CLEAR and cleanup resume recording Additionally write unit tests 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 @@ -74,12 +74,19 @@ 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: @@ -371,6 +378,8 @@ 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: @@ -784,7 +793,7 @@ 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 @@ -797,6 +806,8 @@ 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) 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 @@ -2,7 +2,7 @@ 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, @@ -122,7 +122,7 @@ 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: diff --git a/rpython/jit/backend/llsupport/test/test_resumebuilder.py b/rpython/jit/backend/llsupport/test/test_resumebuilder.py --- a/rpython/jit/backend/llsupport/test/test_resumebuilder.py +++ b/rpython/jit/backend/llsupport/test/test_resumebuilder.py @@ -29,18 +29,20 @@ [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, diff --git a/rpython/jit/backend/resumebuilder.py b/rpython/jit/backend/resumebuilder.py --- a/rpython/jit/backend/resumebuilder.py +++ b/rpython/jit/backend/resumebuilder.py @@ -1,6 +1,6 @@ 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): @@ -19,8 +19,16 @@ 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] = {} @@ -34,7 +42,8 @@ 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 = [] @@ -77,7 +86,9 @@ 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: @@ -135,13 +146,13 @@ 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 @@ -173,9 +184,8 @@ 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] diff --git a/rpython/jit/codewriter/format.py b/rpython/jit/codewriter/format.py --- a/rpython/jit/codewriter/format.py +++ b/rpython/jit/codewriter/format.py @@ -98,7 +98,7 @@ 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): @@ -161,6 +161,8 @@ 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 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 @@ -125,7 +125,7 @@ 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))] + \ @@ -141,7 +141,7 @@ 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: @@ -170,8 +170,9 @@ 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: @@ -201,7 +202,7 @@ 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 @@ -245,8 +246,9 @@ 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) @@ -259,10 +261,10 @@ 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] @@ -344,7 +346,8 @@ 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: @@ -362,7 +365,7 @@ 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) # @@ -809,7 +812,8 @@ # 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] 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 @@ -615,7 +615,8 @@ return 'TargetToken(%d)' % compute_unique_id(self) class TreeLoop(object): - inputargs = None + inputframes = None + inputlocs = None operations = None call_pure_results = None logops = None @@ -655,7 +656,7 @@ 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" @@ -664,7 +665,7 @@ 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): @@ -672,10 +673,14 @@ @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) @@ -713,7 +718,7 @@ 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), \ @@ -735,7 +740,7 @@ 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'): @@ -747,7 +752,8 @@ 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): diff --git a/rpython/jit/metainterp/jitdriver.py b/rpython/jit/metainterp/jitdriver.py --- a/rpython/jit/metainterp/jitdriver.py +++ b/rpython/jit/metainterp/jitdriver.py @@ -3,16 +3,21 @@ 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 diff --git a/rpython/jit/metainterp/optimizeopt/__init__.py b/rpython/jit/metainterp/optimizeopt/__init__.py --- a/rpython/jit/metainterp/optimizeopt/__init__.py +++ b/rpython/jit/metainterp/optimizeopt/__init__.py @@ -51,11 +51,13 @@ 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) diff --git a/rpython/jit/metainterp/optimizeopt/util.py b/rpython/jit/metainterp/optimizeopt/util.py --- a/rpython/jit/metainterp/optimizeopt/util.py +++ b/rpython/jit/metainterp/optimizeopt/util.py @@ -125,11 +125,14 @@ # ____________________________________________________________ -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, '-') @@ -147,11 +150,15 @@ 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 diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py --- a/rpython/jit/metainterp/pyjitpl.py +++ b/rpython/jit/metainterp/pyjitpl.py @@ -13,6 +13,7 @@ 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 @@ -111,39 +112,6 @@ 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() @@ -1460,29 +1428,32 @@ # 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) # ____________________________________________________________ @@ -1679,6 +1650,7 @@ 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 @@ -1704,8 +1676,7 @@ 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) @@ -1722,7 +1693,7 @@ 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: @@ -1822,18 +1793,14 @@ 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 @@ -2034,7 +2001,7 @@ 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() @@ -2175,7 +2142,8 @@ 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 diff --git a/rpython/jit/metainterp/resoperation.py b/rpython/jit/metainterp/resoperation.py --- a/rpython/jit/metainterp/resoperation.py +++ b/rpython/jit/metainterp/resoperation.py @@ -473,9 +473,11 @@ '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 ----- diff --git a/rpython/jit/metainterp/resume2.py b/rpython/jit/metainterp/resume2.py --- a/rpython/jit/metainterp/resume2.py +++ b/rpython/jit/metainterp/resume2.py @@ -1,6 +1,7 @@ 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 @@ -18,8 +19,13 @@ 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, @@ -39,6 +45,13 @@ 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 @@ -62,6 +75,9 @@ 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: @@ -69,6 +85,9 @@ 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 @@ -80,6 +99,10 @@ 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 @@ -96,40 +119,79 @@ 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 = [] @@ -140,33 +202,25 @@ 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: @@ -178,3 +232,75 @@ 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 diff --git a/rpython/jit/metainterp/test/support.py b/rpython/jit/metainterp/test/support.py --- a/rpython/jit/metainterp/test/support.py +++ b/rpython/jit/metainterp/test/support.py @@ -199,6 +199,7 @@ 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 diff --git a/rpython/jit/metainterp/test/test_loop.py b/rpython/jit/metainterp/test/test_loop.py --- a/rpython/jit/metainterp/test/test_loop.py +++ b/rpython/jit/metainterp/test/test_loop.py @@ -189,10 +189,6 @@ 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 diff --git a/rpython/jit/metainterp/test/test_resume2.py b/rpython/jit/metainterp/test/test_resume2.py --- a/rpython/jit/metainterp/test/test_resume2.py +++ b/rpython/jit/metainterp/test/test_resume2.py @@ -2,9 +2,18 @@ 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): @@ -61,17 +70,20 @@ [] 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") @@ -94,7 +106,7 @@ 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] @@ -178,5 +190,147 @@ 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) diff --git a/rpython/jit/metainterp/warmspot.py b/rpython/jit/metainterp/warmspot.py --- a/rpython/jit/metainterp/warmspot.py +++ b/rpython/jit/metainterp/warmspot.py @@ -350,11 +350,8 @@ 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) @@ -370,15 +367,16 @@ 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): @@ -567,8 +565,6 @@ 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) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit