[pypy-commit] pypy resume-refactor: (fijal, rguillebert) Start implementing bytecode representation for

fijal noreply at buildbot.pypy.org
Fri Jan 17 11:50:41 CET 2014


Author: Maciej Fijalkowski <fijall at 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


More information about the pypy-commit mailing list