[pypy-commit] pypy guard-compatible: a hack: track the source of the value that guard_compatible is about
cfbolz
pypy.commits at gmail.com
Thu Aug 18 05:45:01 EDT 2016
Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: guard-compatible
Changeset: r86267:d547e20900d8
Date: 2016-08-08 18:58 +0200
http://bitbucket.org/pypy/pypy/changeset/d547e20900d8/
Log: a hack: track the source of the value that guard_compatible is about
(this might well be reverted, but I want to test its performance)
diff --git a/rpython/jit/metainterp/compatible.py b/rpython/jit/metainterp/compatible.py
--- a/rpython/jit/metainterp/compatible.py
+++ b/rpython/jit/metainterp/compatible.py
@@ -35,6 +35,7 @@
self.jump_target = -1
self.frozen = False
+
def frozen_copy(self):
res = CompatibilityCondition(self.known_valid)
res.conditions = self.conditions[:]
@@ -177,6 +178,48 @@
return False
return True
+ def attach_to_descr(self, descr, guard_value_op, optimizer):
+ from rpython.jit.metainterp.resoperation import AbstractResOp
+ assert descr._compatibility_conditions is None
+ descr._compatibility_conditions = self
+ try:
+ descr.failarg_index = guard_value_op.getfailargs().index(
+ guard_value_op.getarg(0))
+ except ValueError:
+ return # too bad
+ arg = guard_value_op.getarg(0)
+ if not isinstance(arg, AbstractResOp):
+ return
+ if arg.getopnum() not in (rop.GETFIELD_GC_R, rop.GETFIELD_GC_I, rop.GETFIELD_GC_F):
+ return
+ # again, a bit of pattern matching. The trace quite often looks like this:
+ # x = getfield(obj, <fielddescr>)
+ # guard_compatible(x) [x, obj]
+
+ # if this guard fails, we lose the connection between obj and x, which
+ # means that the new bridge will do two things: a guard_compatible on
+ # x, then later do the read again and have a guard_compatible on the
+ # newly read field. This is bad, because one guard_compatible would be
+ # enough. Thus we keep track of this connection, and seed the heapcache
+ # when starting to trace the bridge with that info.
+
+ source_op = arg.getarg(0)
+ try:
+ source_index = guard_value_op.getfailargs().index(source_op)
+ except ValueError:
+ return
+ fielddescr = arg.getdescr()
+ # check whether the same getfield would still yield the same result at
+ # this point in the trace
+ optheap = optimizer.optheap
+ structinfo = optheap.getptrinfo(source_op)
+ cf = optheap.field_cache(fielddescr)
+ field = cf.getfield_from_cache(optheap, structinfo, fielddescr)
+ if field is arg:
+ # yay! we can pass this info on
+ descr.source_failarg_index = source_index
+ descr.source_fielddescr = fielddescr
+
def repr_of_conditions(self, argrepr="?"):
return "\n".join([cond.repr(argrepr) for cond in self.conditions])
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
@@ -1097,6 +1097,10 @@
# XXX it would be better to patch the guard properly in the backend,
# but later
self.fallback_jump_target = 0
+ # the next two attributes are for tracking where the guarded value came
+ # from
+ self.source_failarg_index = -1
+ self.source_fielddescr = None
def find_compatible(self, cpu, ref):
""" callback for the CPU: given a value ref, it returns:
@@ -1125,13 +1129,21 @@
# to this descr
compat_cond = None
if self.failarg_index != -1:
- arg = new_loop.inputargs[self.failarg_index]
firstop = new_loop.operations[0]
+ opindex = 0
+ if self.source_fielddescr:
+ assert firstop.getopnum() == rop.GETFIELD_GC_R
+ assert firstop.getdescr() is self.source_fielddescr
+ arg = firstop
+ opindex = 1
+ firstop = new_loop.operations[1]
+ else:
+ arg = new_loop.inputargs[self.failarg_index]
if (firstop.getopnum() == rop.GUARD_COMPATIBLE and
firstop.getarg(0) is arg):
# a guard_compatible about the same box
# remove it, it doesn't have to be checked in the bridge
- del new_loop.operations[0]
+ del new_loop.operations[opindex]
newdescr = firstop.getdescr()
assert isinstance(newdescr, GuardCompatibleDescr)
compat_cond = newdescr._compatibility_conditions
@@ -1150,16 +1162,11 @@
self.fallback_jump_target = asminfo.asmaddr
return asminfo
- def make_a_counter_per_value(self, guard_value_op, index):
- try:
- self.failarg_index = guard_value_op.getfailargs().index(
- guard_value_op.getarg(0))
- except ValueError:
- pass # we don't set the failarg_index, too bad
-
+ def make_a_counter_per_value(self, guard_op, index):
+ pass
# this is not actually enabling the counter_per_value logic,
# which right now gives bad results with a GUARD_COMPATIBLE
- #ResumeGuardDescr.make_a_counter_per_value(self, guard_value_op, index)
+ #ResumeGuardDescr.make_a_counter_per_value(self, guard_op, index)
def repr_of_conditions(self, argrepr="?"):
if self._compatibility_conditions:
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -731,10 +731,11 @@
op = self._maybe_replace_guard_value(op, descr)
elif op.getopnum() == rop.GUARD_COMPATIBLE:
# XXX randomly stuff things into the descr
- info = self.getptrinfo(op.getarg(0))
- assert isinstance(descr, compile.GuardCompatibleDescr)
- if info is not None and info._compatibility_conditions:
- descr._compatibility_conditions = info._compatibility_conditions
+ #info = self.getptrinfo(op.getarg(0))
+ #assert isinstance(descr, compile.GuardCompatibleDescr)
+ #if info is not None and info._compatibility_conditions:
+ # info._compatibility_conditions.attach_to_descr(descr, op, self)
+ pass
return op
def _maybe_replace_guard_value(self, op, descr):
diff --git a/rpython/jit/metainterp/optimizeopt/pure.py b/rpython/jit/metainterp/optimizeopt/pure.py
--- a/rpython/jit/metainterp/optimizeopt/pure.py
+++ b/rpython/jit/metainterp/optimizeopt/pure.py
@@ -209,6 +209,7 @@
return False
def optimize_GUARD_COMPATIBLE(self, op):
+ from rpython.jit.metainterp.compile import GuardCompatibleDescr
arg0 = self.get_box_replacement(op.getarg(0))
if arg0.is_constant():
# already subject of guard_value
@@ -238,6 +239,10 @@
op.getarg(1))
self.emit_operation(op)
info.mark_last_guard(self.optimizer)
+ descr = op.getdescr()
+ assert isinstance(descr, GuardCompatibleDescr)
+ info._compatibility_conditions.attach_to_descr(
+ descr, op, self.optimizer)
def optimize_GUARD_NO_EXCEPTION(self, op):
if self.last_emitted_operation is REMOVED:
diff --git a/rpython/jit/metainterp/resume.py b/rpython/jit/metainterp/resume.py
--- a/rpython/jit/metainterp/resume.py
+++ b/rpython/jit/metainterp/resume.py
@@ -1047,7 +1047,22 @@
def rebuild_from_resumedata(metainterp, storage, deadframe,
virtualizable_info, greenfield_info):
+ from rpython.jit.metainterp.compile import GuardCompatibleDescr
resumereader = ResumeDataBoxReader(storage, deadframe, metainterp)
+ # this is a gross and terrible HACK
+ # it should be replaced with something significantly more general
+ sourcebox = None
+ if isinstance(storage, GuardCompatibleDescr) and storage.source_fielddescr:
+ assert storage.source_failarg_index >= 0
+ assert storage.failarg_index >= 0
+ sourcebox = resumereader.decode_box(tag(storage.source_failarg_index, TAGBOX), REF)
+ resbox = metainterp.execute_and_record(
+ rop.GETFIELD_GC_R, storage.source_fielddescr, sourcebox)
+ assert resumereader.liveboxes[storage.failarg_index] is None
+ oldbox = resumereader.decode_box(tag(storage.failarg_index, TAGBOX), REF)
+ assert resbox.getref_base() == oldbox.getref_base()
+ resumereader.liveboxes[storage.failarg_index] = resbox
+ # end hack
boxes = resumereader.consume_vref_and_vable_boxes(virtualizable_info,
greenfield_info)
virtualizable_boxes, virtualref_boxes = boxes
diff --git a/rpython/jit/metainterp/test/test_compatible.py b/rpython/jit/metainterp/test/test_compatible.py
--- a/rpython/jit/metainterp/test/test_compatible.py
+++ b/rpython/jit/metainterp/test/test_compatible.py
@@ -618,7 +618,8 @@
class Map(object):
_immutable_fields_ = ['version?']
- def __init__(self):
+ def __init__(self, num):
+ self.num = num
self.version = Version()
self.dct = {}
@@ -642,10 +643,10 @@
map = jit.hint(map, promote_compatible=True)
return map.lookup_version(name)
- m1 = Map()
+ m1 = Map(1)
m1.dct['a'] = 1
m1.dct['b'] = 2
- m2 = Map()
+ m2 = Map(2)
m2.dct['a'] = 1
m2.dct['b'] = 2
m2.dct['c'] = 5
@@ -656,7 +657,7 @@
driver = jit.JitDriver(greens = [], reds = ['n', 'res', 'p'])
def f(n, p):
- res = 0
+ res = p.map.num
while n > 0:
driver.jit_merge_point(n=n, p=p, res=res)
res += p.lookup('a')
More information about the pypy-commit
mailing list