[pypy-commit] pypy guard-compatible: some optimization support for guard_compatible, including storing which

cfbolz pypy.commits at gmail.com
Sat Mar 12 17:25:23 EST 2016


Author: Carl Friedrich Bolz <cfbolz at gmx.de>
Branch: guard-compatible
Changeset: r83002:03482b008a97
Date: 2016-03-12 23:00 +0100
http://bitbucket.org/pypy/pypy/changeset/03482b008a97/

Log:	some optimization support for guard_compatible, including storing
	which elidable functions were applied to its argument

diff --git a/rpython/jit/metainterp/compatible.py b/rpython/jit/metainterp/compatible.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/compatible.py
@@ -0,0 +1,10 @@
+from rpython.jit.metainterp.history import newconst
+
+class CompatibilityCondition(object):
+    """ A collections of conditions that an object needs to fulfil. """
+    def __init__(self, ptr):
+        self.known_valid = ptr
+        self.pure_call_conditions = []
+
+    def record_pure_call(self, op, res):
+        self.pure_call_conditions.append((op, res))
diff --git a/rpython/jit/metainterp/optimizeopt/info.py b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -35,7 +35,8 @@
 
 
 class PtrInfo(AbstractInfo):
-    _attrs_ = ()
+    _attrs_ = ('_compatibility_conditions', )
+    _compatibility_conditions = None
 
     def is_nonnull(self):
         return False
@@ -785,7 +786,7 @@
                                         targetbox, CONST_0, offsetbox,
                                         lgt, mode)
 
-    
+
 class FloatConstInfo(AbstractInfo):
     def __init__(self, const):
         self._const = const
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
@@ -713,6 +713,12 @@
         #
         if op.getopnum() == rop.GUARD_VALUE:
             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
         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
@@ -4,6 +4,7 @@
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from rpython.jit.metainterp.optimizeopt.shortpreamble import PreambleOp
 from rpython.jit.metainterp.optimize import SpeculativeError
+from rpython.jit.metainterp.compatible import CompatibilityCondition
 
 
 class RecentPureOps(object):
@@ -130,6 +131,25 @@
         return recentops
 
     def optimize_CALL_PURE_I(self, op):
+        # Step 0: check if first argument is subject of guard_compatible
+        # XXX maybe don't do this with absolutely *all* call_pure functions
+        # that have a guard_compatible ptr as first arg
+        if op.numargs() > 1:
+            arg1 = self.get_box_replacement(op.getarg(1))
+            if arg1.type == 'r':
+                info = self.getptrinfo(arg1)
+                ccond = info._compatibility_conditions
+                if info and ccond:
+                    # it's subject to guard_compatible
+                    copied_op = op.copy()
+                    copied_op.setarg(1, ccond.known_valid)
+                    result = self._can_optimize_call_pure(copied_op)
+                    if result is not None:
+                        self.make_constant(op, result)
+                        self.last_emitted_operation = REMOVED
+                        ccond.record_pure_call(copied_op, result)
+                        return
+
         # Step 1: check if all arguments are constant
         for arg in op.getarglist():
             self.optimizer.force_box(arg)
@@ -186,6 +206,29 @@
             return True
         return False
 
+    def optimize_GUARD_COMPATIBLE(self, op):
+        arg0 = self.get_box_replacement(op.getarg(0))
+        if arg0.is_constant():
+            # already subject of guard_value
+            return
+        assert arg0.type == 'r'
+        info = self.getptrinfo(arg0)
+        if info:
+            if info.is_virtual():
+                raise InvalidLoop("guard_compatible of a virtual")
+        else:
+            self.make_nonnull(arg0)
+            info = self.getptrinfo(arg0)
+        if info._compatibility_conditions:
+            # seen a previous guard_compatible
+            # check that it's the same previous constant
+            assert info._compatibility_conditions.known_valid.same_constant(op.getarg(1))
+            return
+        else:
+            info._compatibility_conditions = CompatibilityCondition(
+                op.getarg(1))
+            self.emit_operation(op)
+
     def optimize_GUARD_NO_EXCEPTION(self, op):
         if self.last_emitted_operation is REMOVED:
             # it was a CALL_PURE that was killed; so we also kill the
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_compatible.py b/rpython/jit/metainterp/optimizeopt/test/test_compatible.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/metainterp/optimizeopt/test/test_compatible.py
@@ -0,0 +1,56 @@
+from rpython.jit.metainterp.optimizeopt.test.test_util import (
+    LLtypeMixin)
+from rpython.jit.metainterp.optimizeopt.test.test_optimizebasic import (
+    BaseTestBasic)
+from rpython.jit.metainterp.history import ConstInt, ConstPtr
+
+class TestCompatible(BaseTestBasic, LLtypeMixin):
+
+    enable_opts = "intbounds:rewrite:virtualize:string:earlyforce:pure:heap"
+
+    def test_guard_compatible_after_guard_value(self):
+        ops = """
+        [p1]
+        guard_value(p1, ConstPtr(myptr)) []
+        guard_compatible(p1, ConstPtr(myptr)) []
+        jump(ConstPtr(myptr))
+        """
+        expected = """
+        [p1]
+        guard_value(p1, ConstPtr(myptr)) []
+        jump(ConstPtr(myptr))
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_guard_compatible_after_guard_compatible(self):
+        ops = """
+        [p1]
+        guard_compatible(p1, ConstPtr(myptr)) []
+        guard_compatible(p1, ConstPtr(myptr)) []
+        jump(ConstPtr(myptr))
+        """
+        expected = """
+        [p1]
+        guard_compatible(p1, ConstPtr(myptr)) []
+        jump(ConstPtr(myptr))
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_guard_compatible_call_pure(self):
+        call_pure_results = {
+            (ConstInt(123), ConstPtr(self.myptr)): ConstInt(5),
+        }
+        ops = """
+        [p1]
+        guard_compatible(p1, ConstPtr(myptr)) []
+        i3 = call_pure_i(123, p1, descr=plaincalldescr)
+        escape_n(i3)
+        jump(ConstPtr(myptr))
+        """
+        expected = """
+        [p1]
+        guard_compatible(p1, ConstPtr(myptr)) []
+        escape_n(5)
+        jump(ConstPtr(myptr))
+        """
+        self.optimize_loop(ops, expected, call_pure_results=call_pure_results)


More information about the pypy-commit mailing list