Author: Maciej Fijalkowski <fij...@gmail.com> Branch: resume-refactor Changeset: r68709:77a2bb9785d6 Date: 2014-01-17 11:39 +0100 http://bitbucket.org/pypy/pypy/changeset/77a2bb9785d6/
Log: (fijal, rguillebert) Start implementing bytecode representation for the backend resume code 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 @@ -1502,6 +1502,10 @@ self.setup_list_of_addr2name(asm.list_of_addr2name) # self.jitdrivers_sd = codewriter.callcontrol.jitdrivers_sd + self.alljitcodes = [] + for jitcode in codewriter.callcontrol.jitcodes.itervalues(): + jitcode.global_index = len(self.alljitcodes) + self.alljitcodes.append(jitcode) self.virtualref_info = codewriter.callcontrol.virtualref_info self.callinfocollection = codewriter.callcontrol.callinfocollection self.has_libffi_call = codewriter.callcontrol.has_libffi_call diff --git a/rpython/jit/resume/backend.py b/rpython/jit/resume/backend.py --- a/rpython/jit/resume/backend.py +++ b/rpython/jit/resume/backend.py @@ -1,9 +1,35 @@ from rpython.jit.metainterp.resoperation import rop, ResOperation from rpython.jit.metainterp.history import ConstInt, Box, Const -from rpython.jit.resume.frontend import ResumeBytecode, AbstractResumeReader +from rpython.jit.resume.rescode import ResumeBytecodeBuilder, TAGBOX,\ + ResumeBytecode -class LivenessAnalyzer(AbstractResumeReader): + # if op.getopnum() == rop.ENTER_FRAME: + # descr = op.getdescr() + # assert isinstance(descr, JitCode) + # self.enter_frame(op.getarg(0).getint(), descr) + # elif op.getopnum() == rop.LEAVE_FRAME: + # self.leave_frame() + # 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_NEW: + # self.resume_new(op.result, op.getdescr()) + # elif op.getopnum() == rop.RESUME_SETFIELD_GC: + # self.resume_setfield_gc(op.getarg(0), op.getarg(1), + # 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 + # else: + # xxx + +class LivenessAnalyzer(object): def __init__(self, inputframes=None): self.liveness = {} self.frame_starts = [0] @@ -35,6 +61,9 @@ def resume_set_pc(self, pc): pass + def interpret_until(self, *args): + pass + def _track(self, allboxes, box): if box in self.deps: for dep in self.deps[box].values(): @@ -60,12 +89,12 @@ class ResumeBuilder(object): def __init__(self, regalloc, frontend_liveness, descr, inputframes=None, inputlocs=None): - self.newops = [] self.regalloc = regalloc self.current_attachment = {} self.frontend_liveness = frontend_liveness self.frontend_pos = {} self.virtuals = {} + self.builder = ResumeBytecodeBuilder() if inputlocs is not None: i = 0 all = {} @@ -83,6 +112,30 @@ self.current_attachment[box] = loc_pos def process(self, op): + if op.getopnum() == rop.ENTER_FRAME: + self.builder.enter_frame(op.getarg(0).getint(), op.getdescr()) + elif op.getopnum() == rop.RESUME_PUT: + frame_pos = op.getarg(1).getint() + pos_in_frame = op.getarg(2).getint() + box = op.getarg(0) + if isinstance(box, Const): + pos = self.builder.encode_const(box) + self.builder.resume_put(pos, frame_pos, pos_in_frame) + return + try: + loc = self.regalloc.loc(box, must_exist=True).get_jitframe_position() + pos = self.builder.encode(TAGBOX, loc) + self.builder.resume_put(pos, frame_pos, pos_in_frame) + except KeyError: + xxx + self.current_attachment[box] = pos + self.frontend_pos[box] = (frame_pos, pos_in_frame) + elif op.getopnum() == rop.LEAVE_FRAME: + self.builder.leave_frame() + else: + xxx + return + xxxx if op.getopnum() == rop.RESUME_PUT: box = op.getarg(0) args = op.getarglist() @@ -119,8 +172,10 @@ return if v not in self.current_attachment: return + pos = self.builder.encode(TAGBOX, pos) if self.current_attachment[v] != pos: frame_index, frame_pos = self.frontend_pos[v] + xxx self.newops.append(ResOperation(rop.RESUME_PUT, [ ConstInt(pos), frame_index, frame_pos], None)) @@ -137,10 +192,11 @@ for v, loc in self.regalloc.xrm.reg_bindings.iteritems(): if v not in visited: self._mark_visited(v, loc) - return len(self.newops) + return self.builder.getpos() def finish(self, parent, parent_position, clt): - return ResumeBytecode(self.newops, parent, parent_position, clt) + return ResumeBytecode(self.builder.build(), parent, parent_position, + clt) def flatten(inputframes): diff --git a/rpython/jit/resume/frontend.py b/rpython/jit/resume/frontend.py --- a/rpython/jit/resume/frontend.py +++ b/rpython/jit/resume/frontend.py @@ -7,31 +7,6 @@ from rpython.jit.codewriter.jitcode import JitCode from rpython.rlib import rstack -class ResumeBytecode(object): - def __init__(self, opcodes, parent=None, parent_position=-1, loop=None): - self.opcodes = opcodes - self.parent = parent - self.parent_position = parent_position - self.loop = loop - -class ResumeFrame(object): - def __init__(self, jitcode): - self.registers = [-1] * jitcode.num_regs() - self.jitcode = jitcode - self.pc = -1 - -TAGCONST = 0x0 -TAGVIRTUAL = 0x2 -TAGBOX = 0x3 -TAGSMALLINT = 0x1 - -TAGOFFSET = 2 - -class Virtual(object): - def __init__(self, pos, descr): - self.pos = pos - self.fields = {} - self.descr = descr class AbstractResumeReader(object): @@ -144,6 +119,10 @@ def read_int(self, jitframe_pos): return self.metainterp.cpu.get_int_value(self.deadframe, jitframe_pos) +class Dumper(AbstractResumeReader): + def __init__(self): + xxx + class DirectResumeReader(AbstractResumeReader): """ Directly read values from the jitframe and put them in the blackhole interpreter diff --git a/rpython/jit/resume/reader.py b/rpython/jit/resume/reader.py new file mode 100644 --- /dev/null +++ b/rpython/jit/resume/reader.py @@ -0,0 +1,145 @@ + +import sys +from rpython.jit.metainterp.history import ConstInt +from rpython.jit.resume import rescode + +class ResumeFrame(object): + def __init__(self, jitcode): + self.registers = [-1] * jitcode.num_regs() + self.jitcode = jitcode + self.pc = -1 + + +class Virtual(object): + def __init__(self, pos, descr): + self.pos = pos + self.fields = {} + self.descr = descr + + +class AbstractResumeReader(object): + """ A resume reader that can follow resume until given point. Consult + the concrete classes for details + """ + + def __init__(self, staticdata): + self.framestack = [] + self.staticdata = staticdata + self.consts = [] # XXX cache? + self.virtuals = {} + self.virtual_list = [] + + def rebuild(self, faildescr): + self._rebuild_until(faildescr.rd_resume_bytecode, + faildescr.rd_bytecode_position) + return self.finish() + + def finish(self): + pass + + def enter_frame(self, pc, jitcode): + if self.framestack: + assert pc != -1 + self.framestack[-1].pc = pc + self.framestack.append(ResumeFrame(jitcode)) + + def encode_box(self, pos): + return rescode.TAGBOX | (pos << rescode.TAGOFFSET) + + def encode_virtual(self, box): + return rescode.TAGVIRTUAL | (self.virtuals[box].pos << rescode.TAGOFFSET) + + def encode_const(self, const): + XXX + if isinstance(const, ConstInt) and const.getint() < (sys.maxint >> 3): + return rescode.TAGSMALLINT | (const.getint() << rescode.TAGOFFSET) + self.consts.append(const) + return rescode.TAGCONST | ((len(self.consts) - 1) << TAGOFFSET) + + def decode(self, pos): + return pos & 0x3, pos >> rescode.TAGOFFSET + + def resume_put(self, jitframe_pos_box, frame_no, frontend_position): + XXX + if isinstance(jitframe_pos_box, Box): + jitframe_pos = self.encode_virtual(jitframe_pos_box) + else: + jitframe_pos = self.encode_box(jitframe_pos_box.getint()) + self.framestack[frame_no].registers[frontend_position] = jitframe_pos + + def encode(self, box): + xxx + + def resume_new(self, box, descr): + # XXX make it a list + v = Virtual(len(self.virtual_list), descr) + self.virtuals[box] = v + self.virtual_list.append(v) + + def resume_setfield_gc(self, box, fieldbox, descr): + # XXX optimize fields + self.virtuals[box].fields[descr] = self.encode(fieldbox) + + 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): + pos = self.encode_const(const) + self.framestack[frame_no].registers[frontend_position] = pos + + def resume_set_pc(self, pc): + self.framestack[-1].pc = pc + + def leave_frame(self): + self.framestack.pop() + + def _rebuild_until(self, rb, position): + if rb.parent is not None: + self._rebuild_until(rb.parent, rb.parent_position) + self.interpret_until(rb.opcodes, position) + + def read(self, pos): + return ord(self.bytecode.opcodes[pos]) + + def read_short(self, pos): + return self.read(pos) + (self.read(pos + 1) << 16) + + def interpret_until(self, bytecode, until, pos=0): + self.bytecode = bytecode + while pos < until: + op = ord(bytecode.opcodes[pos]) + if op == rescode.UNUSED: + raise Exception("malformed bytecode") + elif op == rescode.ENTER_FRAME: + pc = self.read_short(pos + 1) - 1 + jitcode = self.staticdata.alljitcodes[self.read_short(pos + 3)] + self.enter_frame(pc, jitcode) + pos += 5 + elif op == rescode.RESUME_PUT: + encoded = self.read_short(pos + 1) + frame_pos = self.read(pos + 3) + pos_in_frame = self.read(pos + 4) + self.resume_put(encoded, frame_pos, pos_in_frame) + pos += 5 + else: + xxx + self.bytecode = None + + def read_int(self, jitframe_pos): + return self.metainterp.cpu.get_int_value(self.deadframe, jitframe_pos) + +class Dumper(AbstractResumeReader): + def __init__(self, staticdata): + AbstractResumeReader.__init__(self, staticdata) + self.l = [] + + def enter_frame(self, pc, jitcode): + self.l.append("enter_frame %d %s" % (pc, jitcode.name)) + + def resume_put(self, encoded, frame_pos, pos_in_frame): + tag, index = self.decode(encoded) + self.l.append("resume_put (%d, %d) %d %d" % (tag, index, frame_pos, + pos_in_frame)) + + def finish(self): + return "\n".join(self.l) diff --git a/rpython/jit/resume/rescode.py b/rpython/jit/resume/rescode.py new file mode 100644 --- /dev/null +++ b/rpython/jit/resume/rescode.py @@ -0,0 +1,66 @@ + +from rpython.jit.metainterp.history import ConstInt + +UNUSED, ENTER_FRAME, LEAVE_FRAME, RESUME_PUT = range(4) + +TAGCONST = 0x0 +TAGVIRTUAL = 0x2 +TAGBOX = 0x3 +TAGSMALLINT = 0x1 + +TAGOFFSET = 2 + +class ResumeBytecode(object): + def __init__(self, opcodes, parent=None, parent_position=-1, loop=None): + self.opcodes = opcodes + self.parent = parent + self.parent_position = parent_position + self.loop = loop + + def dump(self, staticdata, resume_pos): + from rpython.jit.resume.reader import Dumper + + d = Dumper(staticdata) + d.interpret_until(self, resume_pos) + return d.finish() + +class ResumeBytecodeBuilder(object): + def __init__(self): + self.l = [] + + def getpos(self): + return len(self.l) + + def build(self): + return "".join(self.l) + + def write(self, i): + assert 0 <= i < 256 + self.l.append(chr(i)) + + def write_short(self, i): + assert 0 <= i < 0x1000 + self.write(i & 0xff) + self.write(i >> 8) + + def enter_frame(self, pc, jitcode): + self.write(ENTER_FRAME) + self.write_short(pc + 1) # can be -1 ! + self.write_short(jitcode.global_index) + + def leave_frame(self): + self.write(LEAVE_FRAME) + + def encode(self, tag, loc): + return tag | (loc << 2) + + def encode_const(self, const): + if isinstance(const, ConstInt) and 0 <= const.getint() < 0x4000: + return TAGSMALLINT | (const.getint() << 2) + xxx + + def resume_put(self, pos, frame_pos, pos_in_frame): + self.write(RESUME_PUT) + self.write_short(pos) + self.write(frame_pos) + self.write(pos_in_frame) diff --git a/rpython/jit/resume/test/test_backend.py b/rpython/jit/resume/test/test_backend.py --- a/rpython/jit/resume/test/test_backend.py +++ b/rpython/jit/resume/test/test_backend.py @@ -10,7 +10,8 @@ class MockJitCode(JitCode): def __init__(self, no): self.no = no - self.name = 'frame %d' % no + self.global_index = no + self.name = 'frame-%d' % no def num_regs(self): return self.no @@ -18,13 +19,20 @@ def __repr__(self): return 'MockJitCode(%d)' % self.no +class MockStaticData(object): + def __init__(self, *jitcodes): + self.alljitcodes = list(jitcodes) + +def preparse(inp): + return "\n".join([s.strip() for s in inp.split("\n") if s.strip()]) + class ResumeTest(object): def setup_method(self, meth): self.cpu = self.CPUClass(None, None) self.cpu.setup_once() def test_simple(self): - jitcode = MockJitCode(3) + jitcode = MockJitCode(1) loop = parse(""" [i0] enter_frame(-1, descr=jitcode) @@ -37,16 +45,16 @@ self.cpu.compile_loop(None, loop.inputargs, loop.operations, looptoken) 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, - expected_resume.operations) + assert descr.rd_bytecode_position == 15 + staticdata = MockStaticData(None, jitcode) + res = descr.rd_resume_bytecode.dump(staticdata, + descr.rd_bytecode_position) + expected_resume = preparse(""" + enter_frame -1 frame-1 + resume_put (3, 28) 0 2 + resume_put (1, 1) 0 1 + """) + assert res == expected_resume def test_resume_new(self): jitcode = JitCode("name") diff --git a/rpython/jit/resume/test/test_frontend.py b/rpython/jit/resume/test/test_frontend.py --- a/rpython/jit/resume/test/test_frontend.py +++ b/rpython/jit/resume/test/test_frontend.py @@ -2,8 +2,9 @@ from rpython.jit.tool.oparser import parse from rpython.jit.codewriter.jitcode import JitCode from rpython.jit.metainterp.history import AbstractDescr, Const, INT, Stats -from rpython.jit.resume.frontend import rebuild_from_resumedata,\ - ResumeBytecode, AbstractResumeReader +from rpython.jit.resume.frontend import rebuild_from_resumedata +from rpython.jit.resume.rescode import ResumeBytecode +from rpython.jit.resume.reader import AbstractResumeReader from rpython.jit.metainterp.resoperation import rop from rpython.jit.codewriter.format import unformat_assembler from rpython.jit.codewriter.codewriter import CodeWriter _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit