[pypy-commit] pypy resume-refactor: (fijal, rguillebert) make resume2 emit RESUME_CLEAR and cleanup resume recording
fijal
noreply at buildbot.pypy.org
Tue Jan 14 12:17:16 CET 2014
Author: Maciej Fijalkowski <fijall at 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)
More information about the pypy-commit
mailing list