[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