Author: Maciej Fijalkowski <[email protected]>
Branch: resume-refactor
Changeset: r66685:94ee63a63378
Date: 2013-08-30 19:26 +0100
http://bitbucket.org/pypy/pypy/changeset/94ee63a63378/
Log: (fijal, arigo) write some tests and make them pass
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
@@ -4,6 +4,7 @@
from rpython.jit.metainterp.history import (INT, REF, FLOAT, JitCellToken,
ConstInt, BoxInt, AbstractFailDescr)
from rpython.jit.metainterp.resoperation import ResOperation, rop
+from rpython.jit.metainterp.resume2 import rebuild_locs_from_resumedata
from rpython.rlib import rgc
from rpython.rlib.debug import (debug_start, debug_stop, have_debug_prints,
debug_print)
@@ -111,8 +112,9 @@
return r
def rebuild_faillocs_from_descr(self, descr, inputargs):
- xxx
- locs = []
+ XXX
+ loc_positions = rebuild_locs_from_resumedata(descr)
+ locs = [None] * len(loc_positions)
GPR_REGS = len(self.cpu.gen_regs)
XMM_REGS = len(self.cpu.float_regs)
input_i = 0
@@ -120,19 +122,17 @@
coeff = 1
else:
coeff = 2
- for pos in descr.rd_locs:
- if pos == -1:
- continue
- elif pos < GPR_REGS * WORD:
- locs.append(self.cpu.gen_regs[pos // WORD])
+ for item, pos in enumerate(loc_positions):
+ if pos < GPR_REGS * WORD:
+ locs[item] = self.cpu.gen_regs[pos // WORD]
elif pos < (GPR_REGS + XMM_REGS * coeff) * WORD:
pos = (pos // WORD - GPR_REGS) // coeff
- locs.append(self.cpu.float_regs[pos])
+ locs[item] = self.cpu.float_regs[pos]
else:
i = pos // WORD - self.cpu.JITFRAME_FIXED_SIZE
assert i >= 0
tp = inputargs[input_i].type
- locs.append(self.new_stack_loc(i, pos, tp))
+ locs[item] = self.new_stack_loc(i, pos, tp)
input_i += 1
return locs
diff --git a/rpython/jit/backend/llsupport/regalloc.py
b/rpython/jit/backend/llsupport/regalloc.py
--- a/rpython/jit/backend/llsupport/regalloc.py
+++ b/rpython/jit/backend/llsupport/regalloc.py
@@ -678,7 +678,7 @@
return [self.loc(op.getarg(0))]
-def compute_vars_longevity(inputargs, operations):
+def compute_vars_longevity(inputargs, operations, descr=None):
# compute a dictionary that maps variables to index in
# operations that is a "last-time-seen"
@@ -689,21 +689,19 @@
produced = {}
last_used = {}
last_real_usage = {}
+ frontend_alive = {}
liveness_analyzer = LivenessAnalyzer()
+ start_pos = 0
for position, op in enumerate(operations):
- if op.getopnum() == rop.ENTER_FRAME:
- liveness_analyzer.enter_frame(op.getdescr())
- elif op.getopnum() == rop.LEAVE_FRAME:
- liveness_analyzer.leave_frame()
- elif op.getopnum() == rop.RESUME_PUT:
- liveness_analyzer.put(op.getarg(0), op.getarg(1).getint(),
- op.getarg(2).getint())
- elif op.is_guard():
+ if op.is_guard():
+ liveness_analyzer.interpret_until(operations, position, start_pos)
+ start_pos = position
framestack = liveness_analyzer.get_live_info()
for frame in framestack:
- for item in frame:
+ for item in liveness_analyzer.all_boxes_from(frame):
if item is not None:
last_used[item] = position
+ frontend_alive[item] = position
for i in range(len(operations)-1, -1, -1):
op = operations[i]
@@ -740,7 +738,7 @@
longevity[arg] = (0, last_used[arg])
del last_used[arg]
assert len(last_used) == 0
- return longevity, last_real_usage
+ return longevity, last_real_usage, frontend_alive
def is_comparison_or_ovf_op(opnum):
from rpython.jit.metainterp.resoperation import opclasses
diff --git a/rpython/jit/backend/llsupport/resumebuilder.py
b/rpython/jit/backend/llsupport/resumebuilder.py
--- a/rpython/jit/backend/llsupport/resumebuilder.py
+++ b/rpython/jit/backend/llsupport/resumebuilder.py
@@ -1,20 +1,39 @@
-from rpython.jit.metainterp.resoperation import rop
+from rpython.jit.metainterp.resoperation import rop, ResOperation
from rpython.jit.metainterp.history import ConstInt
-from rpython.jit.metainterp.resume2 import ResumeBytecode
-from rpython.jit.codewriter.jitcode import JitCode
+from rpython.jit.metainterp.resume2 import ResumeBytecode, AbstractResumeReader
-class LivenessAnalyzer(object):
+class LivenessAnalyzer(AbstractResumeReader):
def __init__(self):
+ self.liveness = {}
+ self.frame_starts = [0]
self.framestack = []
+ self.deps = {}
- def enter_frame(self, jitcode):
- assert isinstance(jitcode, JitCode)
+ def enter_frame(self, pc, jitcode):
+ self.frame_starts.append(self.frame_starts[-1] + jitcode.num_regs())
self.framestack.append([None] * jitcode.num_regs())
- def put(self, value, depth, position):
- # - depth - 1 can be expressed as ~depth (haha)
- self.framestack[- depth - 1][position] = value
+ def resume_put(self, box, framepos, frontend_pos):
+ self.framestack[-framepos - 1][frontend_pos] = box
+
+ def resume_new(self, result, descr):
+ self.deps[result] = {}
+
+ def resume_setfield_gc(self, arg0, arg1, descr):
+ self.deps[arg0][descr] = arg1
+
+ def _track(self, allboxes, box):
+ if box in self.deps:
+ for dep in self.deps[box].values():
+ self._track(allboxes, dep)
+ allboxes.append(box)
+
+ def all_boxes_from(self, frame):
+ allboxes = []
+ for item in frame:
+ self._track(allboxes, item)
+ return allboxes
def get_live_info(self):
return self.framestack
@@ -23,45 +42,39 @@
self.framestack.pop()
class ResumeBuilder(object):
- def __init__(self, regalloc):
- self.framestack = []
+ def __init__(self, regalloc, frontend_liveness, descr):
self.newops = []
self.regalloc = regalloc
+ self.current_attachment = {}
+ self.frontend_liveness = frontend_liveness
def process(self, op):
- oplist[op.getopnum()](self, op)
-
- def process_enter_frame(self, op):
- self.framestack.append(op.getdescr())
self.newops.append(op)
- def _find_position_for_box(self, v):
- return self.regalloc.loc(v).get_jitframe_position()
+ def _mark_visited(self, v, loc):
+ pos = loc.get_jitframe_position()
+ if (v not in self.frontend_liveness or
+ self.frontend_liveness[v] > self.regalloc.rm.position):
+ return
+ if (v not in self.current_attachment or
+ self.current_attachment[v] != pos):
+ self.newops.append(ResOperation(rop.BACKEND_ATTACH, [
+ v, ConstInt(pos)], None))
+ self.current_attachment[v] = pos
- def process_resume_put(self, op):
- pos = self._find_position_for_box(op.getarg(0))
- self.newops.append(op.copy_and_change(rop.BACKEND_PUT,
- args=[ConstInt(pos),
- op.getarg(1),
- op.getarg(2)]))
-
- def process_leave_frame(self, op):
- self.framestack.pop()
- self.newops.append(op)
-
- def get_position(self):
+ def mark_resumable_position(self):
+ visited = {}
+ for v, loc in self.regalloc.fm.bindings.iteritems():
+ self._mark_visited(v, loc)
+ visited[v] = None
+ for v, loc in self.regalloc.rm.reg_bindings.iteritems():
+ if v not in visited:
+ self._mark_visited(v, loc)
+ for v, loc in self.regalloc.xrm.reg_bindings.iteritems():
+ if v not in visited:
+ self._mark_visited(v, loc)
return len(self.newops)
def finish(self, parent, clt):
return ResumeBytecode(self.newops, parent, clt)
- def not_implemented_op(self, op):
- print "Not implemented", op.getopname()
- raise NotImplementedError(op.getopname())
-
-oplist = [ResumeBuilder.not_implemented_op] * rop._LAST
-for name, value in ResumeBuilder.__dict__.iteritems():
- if name.startswith('process_'):
- num = getattr(rop, name[len('process_'):].upper())
- oplist[num] = value
-
diff --git a/rpython/jit/backend/llsupport/test/test_resume.py
b/rpython/jit/backend/llsupport/test/test_resume.py
--- a/rpython/jit/backend/llsupport/test/test_resume.py
+++ b/rpython/jit/backend/llsupport/test/test_resume.py
@@ -1,12 +1,15 @@
from rpython.jit.metainterp.history import JitCellToken
+from rpython.jit.metainterp.history import BasicFailDescr
from rpython.jit.codewriter.jitcode import JitCode
from rpython.jit.tool.oparser import parse
from rpython.jit.metainterp.optimizeopt.util import equaloplists
+from rpython.rtyper.lltypesystem import lltype
class MockJitCode(JitCode):
def __init__(self, no):
self.no = no
+ self.name = 'frame %d' % no
def num_regs(self):
return self.no
@@ -32,11 +35,72 @@
self.cpu.compile_loop(None, loop.inputargs, loop.operations,
looptoken)
descr = loop.operations[2].getdescr()
- assert descr.rd_bytecode_position == 2
+ assert descr.rd_bytecode_position == 3
expected_resume = parse("""
- []
+ [i0]
enter_frame(-1, descr=jitcode)
- backend_put(28, 0, 2)
+ resume_put(i0, 0, 2)
+ backend_attach(i0, 28)
leave_frame()
- """, namespace={'jitcode': jitcode}).operations
- equaloplists(descr.rd_resume_bytecode.opcodes, expected_resume)
+ """, namespace={'jitcode': jitcode})
+ i0 = descr.rd_resume_bytecode.opcodes[1].getarg(0)
+ i0b = expected_resume.inputargs[0]
+ equaloplists(descr.rd_resume_bytecode.opcodes,
+ expected_resume.operations,
+ remap={i0b:i0})
+
+ def test_resume_new(self):
+ jitcode = JitCode("name")
+ jitcode.setup(num_regs_i=1, num_regs_r=0, num_regs_f=0)
+ S = lltype.GcStruct('S', ('field', lltype.Signed))
+ structdescr = self.cpu.sizeof(S)
+ fielddescr = self.cpu.fielddescrof(S, 'field')
+ namespace = {'jitcode':jitcode, 'structdescr':structdescr,
+ 'fielddescr':fielddescr}
+ loop = parse("""
+ [i0, i1]
+ enter_frame(-1, descr=jitcode)
+ p0 = resume_new(descr=structdescr)
+ resume_setfield_gc(p0, i0, descr=fielddescr)
+ resume_put(p0, 0, 0)
+ i2 = int_lt(i1, 13)
+ guard_true(i2)
+ leave_frame()
+ finish()
+ """, namespace=namespace)
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(None, loop.inputargs, loop.operations,
+ looptoken)
+ expected_resume = parse("""
+ [i0]
+ enter_frame(-1, descr=jitcode)
+ p0 = resume_new(descr=structdescr)
+ resume_setfield_gc(p0, i0, descr=fielddescr)
+ resume_put(p0, 0, 0)
+ backend_attach(i0, 28)
+ leave_frame()
+ """, namespace=namespace)
+ descr = loop.operations[-3].getdescr()
+ assert descr.rd_bytecode_position == 5
+ i0 = descr.rd_resume_bytecode.opcodes[2].getarg(1)
+ i0b = expected_resume.inputargs[0]
+ equaloplists(descr.rd_resume_bytecode.opcodes,
+ expected_resume.operations,
+ remap={i0b:i0})
+
+ def test_spill(self):
+ jitcode = JitCode("name")
+ jitcode.setup(num_regs_i=2, num_regs_r=0, num_regs_f=0)
+ faildescr = BasicFailDescr(1)
+ loop = parse("""
+ [i0, i1]
+ enter_frame(-1, descr=jitcode)
+ i2 = int_add(i0, i1)
+ resume_put(i2, 0, 1)
+ force_spill(i2)
+ guard_true(i0, descr=faildescr)
+ leave_frame()
+ """, namespace={'jitcode':jitcode, 'faildescr':faildescr})
+ looptoken = JitCellToken()
+ self.cpu.compile_loop(None, loop.inputargs, loop.operations,
+ looptoken)
diff --git a/rpython/jit/backend/x86/assembler.py
b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -532,7 +532,8 @@
operations = regalloc.prepare_bridge(inputargs, arglocs,
operations,
self.current_clt.allgcrefs,
- self.current_clt.frame_info)
+ self.current_clt.frame_info,
+ faildescr)
self._check_frame_depth(self.mc, regalloc.get_gcmap())
frame_depth_no_fixed_size = self._assemble(regalloc, inputargs,
operations)
codeendpos = self.mc.get_relative_pos()
@@ -1723,7 +1724,7 @@
is_guard_not_invalidated = guard_opnum == rop.GUARD_NOT_INVALIDATED
is_guard_not_forced = guard_opnum == rop.GUARD_NOT_FORCED
gcmap = self._regalloc.get_gcmap()
- pos = self._regalloc.resumebuilder.get_position()
+ pos = self._regalloc.resumebuilder.mark_resumable_position()
faildescr.rd_bytecode_position = pos
return GuardToken(self.cpu, gcmap, faildescr,
self._regalloc.uses_floats(),
diff --git a/rpython/jit/backend/x86/regalloc.py
b/rpython/jit/backend/x86/regalloc.py
--- a/rpython/jit/backend/x86/regalloc.py
+++ b/rpython/jit/backend/x86/regalloc.py
@@ -125,7 +125,6 @@
def __init__(self, assembler, translate_support_code=False):
assert isinstance(translate_support_code, bool)
- self.resumebuilder = ResumeBuilder(self)
# variables that have place in register
self.assembler = assembler
self.translate_support_code = translate_support_code
@@ -133,14 +132,15 @@
self.jump_target_descr = None
self.final_jump_op = None
- def _prepare(self, inputargs, operations, allgcrefs):
+ def _prepare(self, inputargs, operations, allgcrefs, descr=None):
cpu = self.assembler.cpu
self.fm = X86FrameManager(cpu.get_baseofs_of_frame_field())
operations = cpu.gc_ll_descr.rewrite_assembler(cpu, operations,
allgcrefs)
# compute longevity of variables
- longevity, last_real_usage = compute_vars_longevity(
- inputargs, operations)
+ x = compute_vars_longevity(inputargs, operations, descr)
+ longevity, last_real_usage, frontend_liveness = x
+ self.resumebuilder = ResumeBuilder(self, frontend_liveness, descr)
self.longevity = longevity
self.last_real_usage = last_real_usage
self.rm = gpr_reg_mgr_cls(self.longevity,
@@ -163,8 +163,8 @@
return operations
def prepare_bridge(self, inputargs, arglocs, operations, allgcrefs,
- frame_info):
- operations = self._prepare(inputargs, operations, allgcrefs)
+ frame_info, descr):
+ operations = self._prepare(inputargs, operations, allgcrefs, descr)
self._update_bindings(arglocs, inputargs)
self.min_bytes_before_label = 0
return operations
@@ -334,7 +334,6 @@
self.assembler.mc.mark_op(None) # end of the loop
for arg in inputargs:
self.possibly_free_var(arg)
- self.assembler.current_clt.rd_bytecode = self.resumebuilder.newops
def flush_loop(self):
# rare case: if the loop is too short, or if we are just after
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,8 +125,7 @@
# ____________________________________________________________
-def equaloplists(oplist1, oplist2, strict_fail_args=True, remap={},
- text_right=None):
+def equaloplists(oplist1, oplist2, remap={}, text_right=None):
# 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
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
@@ -472,7 +472,9 @@
'ENTER_FRAME/1d',
'LEAVE_FRAME/0',
'RESUME_PUT/3',
- 'BACKEND_PUT/3',
+ 'RESUME_NEW/0d',
+ 'RESUME_SETFIELD_GC/2d',
+ 'BACKEND_ATTACH/2',
# same as resume_put, but the first arg is backend-dependent,
# instead of a box
'_RESUME_LAST',
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
@@ -20,8 +20,7 @@
self._rebuild_until(rb.parent, rb.parent_position)
self.interpret_until(rb.opcodes, position)
- def interpret_until(self, bytecode, until):
- pos = 0
+ def interpret_until(self, bytecode, until, pos=0):
while pos < until:
op = bytecode[pos]
if op.getopnum() == rop.ENTER_FRAME:
@@ -30,14 +29,23 @@
self.enter_frame(op.getarg(0).getint(), descr)
elif op.getopnum() == rop.LEAVE_FRAME:
self.leave_frame()
- elif op.getopnum() == rop.BACKEND_PUT:
- self.put(op.getarg(0).getint(), op.getarg(1).getint(),
+ 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 not op.is_resume():
+ pos += 1
+ continue
else:
xxx
pos += 1
- def put(self, jitframe_index, depth, frontend_position):
+ def resume_put(self, jitframe_index, depth, frontend_position):
+ XXX
jitcode = self.metainterp.framestack[-1].jitcode
frame = self.metainterp.framestack[- depth - 1]
if frontend_position < jitcode.num_regs_i():
@@ -82,6 +90,19 @@
def leave_frame(self):
self.framestack.pop()
+class SimpleResumeReader(AbstractResumeReader):
+ def __init__(self):
+ self.framestack = []
+
+ def enter_frame(self, pc, jitcode):
+ self.framestack.append(jitcode.num_regs())
+
+ def put(self, *args):
+ pass
+
+ def leave_frame(self):
+ self.framestack.pop()
+
def rebuild_from_resumedata(metainterp, deadframe, faildescr):
BoxResumeReader(metainterp, deadframe).rebuild(faildescr)
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
@@ -124,6 +124,17 @@
assert f.registers_i[0].getint() == 42 + 3
assert f.registers_i[1].getint() == 2 + 3
+ def test_new(self):
+ base = parse("""
+ []
+ enter_frame(-1, descr=jitcode)
+ i0 = new(descr=structdescr)
+ XXX
+ resume_setfield(i0, 13
+ backend_put(12,
+ leave_frame()
+ """)
+
def test_reconstructing_resume_reader(self):
jitcode1 = JitCode("jitcode")
jitcode1.setup(num_regs_i=3, num_regs_f=0, num_regs_r=0)
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit