[pypy-svn] pypy out-of-line-guards-2: Progress on out-of-line-guards-2. This is the poor-man's version, we can do

fijal commits-noreply at bitbucket.org
Wed Apr 13 14:18:17 CEST 2011


Author: Maciej Fijalkowski <fijall at gmail.com>
Branch: out-of-line-guards-2
Changeset: r43327:6d4d86899596
Date: 2011-04-13 14:17 +0200
http://bitbucket.org/pypy/pypy/changeset/6d4d86899596/

Log:	Progress on out-of-line-guards-2. This is the poor-man's version, we
	can do better.

	* Record a guard on each of quasi_immutable_fields
	* This guard fails if the loop is invalidated (only llgraph backend so
	far)
	* It creates a normal bridge. This is the point where we can improve
	by invalidating call assemblers for example and resetting counters

diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py
--- a/pypy/jit/metainterp/compile.py
+++ b/pypy/jit/metainterp/compile.py
@@ -80,6 +80,7 @@
     if loop.quasi_immutable_deps is not None:
         for qmut in loop.quasi_immutable_deps:
             qmut.register_loop_token(wref)
+        # XXX maybe we should clear the dictionary here
     # mostly for tests: make sure we don't keep a reference to the LoopToken
     loop.token = None
     if not we_are_translated():
@@ -400,6 +401,12 @@
         self.copy_all_attributes_into(res)
         return res
 
+class ResumeGuardNotInvalidated(ResumeGuardDescr):
+    def _clone_if_mutable(self):
+        res = ResumeGuardNotInvalidated()
+        self.copy_all_attributes_into(res)
+        return res
+
 class ResumeAtPositionDescr(ResumeGuardDescr):
     def _clone_if_mutable(self):
         res = ResumeAtPositionDescr()

diff --git a/pypy/jit/backend/llgraph/runner.py b/pypy/jit/backend/llgraph/runner.py
--- a/pypy/jit/backend/llgraph/runner.py
+++ b/pypy/jit/backend/llgraph/runner.py
@@ -286,6 +286,10 @@
             raise ValueError("CALL_ASSEMBLER not supported")
         llimpl.redirect_call_assembler(self, oldlooptoken, newlooptoken)
 
+    def invalidate_loop(self, looptoken):
+        for loop in looptoken.compiled_loop_token.loop_and_bridges:
+            loop._obj.externalobj.invalid = True
+
     # ----------
 
     def sizeof(self, S):

diff --git a/pypy/jit/metainterp/test/test_optimizeutil.py b/pypy/jit/metainterp/test/test_optimizeutil.py
--- a/pypy/jit/metainterp/test/test_optimizeutil.py
+++ b/pypy/jit/metainterp/test/test_optimizeutil.py
@@ -3,6 +3,7 @@
 from pypy.rpython.lltypesystem import lltype, llmemory, rclass, rstr
 from pypy.rpython.ootypesystem import ootype
 from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
+from pypy.rpython.rclass import FieldListAccessor, IR_QUASI_IMMUTABLE
 
 from pypy.jit.backend.llgraph import runner
 from pypy.jit.metainterp.history import (BoxInt, BoxPtr, ConstInt, ConstPtr,
@@ -12,6 +13,7 @@
 from pypy.jit.codewriter.effectinfo import EffectInfo
 from pypy.jit.codewriter.heaptracker import register_known_gctype, adr2int
 from pypy.jit.tool.oparser import parse
+from pypy.jit.metainterp.quasiimmut import QuasiImmutDescr
 
 def test_sort_descrs():
     class PseudoDescr(AbstractDescr):
@@ -62,6 +64,18 @@
     nextdescr = cpu.fielddescrof(NODE, 'next')
     otherdescr = cpu.fielddescrof(NODE2, 'other')
 
+    accessor = FieldListAccessor()
+    accessor.initialize(None, {'inst_field': IR_QUASI_IMMUTABLE})
+    QUASI = lltype.GcStruct('QUASIIMMUT', ('inst_field', lltype.Signed),
+                            ('mutate_field', rclass.OBJECTPTR),
+                            hints={'immutable_fields': accessor})
+    quasi = lltype.malloc(QUASI, immortal=True)
+    quasifielddescr = cpu.fielddescrof(QUASI, 'inst_field')
+    quasibox = BoxPtr(lltype.cast_opaque_ptr(llmemory.GCREF, quasi))
+    quasiimmutdescr = QuasiImmutDescr(cpu, quasibox,
+                                      quasifielddescr,
+                                      cpu.fielddescrof(QUASI, 'mutate_field'))
+
     NODEOBJ = lltype.GcStruct('NODEOBJ', ('parent', OBJECT),
                                          ('ref', lltype.Ptr(OBJECT)))
     nodeobj = lltype.malloc(NODEOBJ)

diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -555,14 +555,15 @@
     opimpl_setfield_raw_r = _opimpl_setfield_raw_any
     opimpl_setfield_raw_f = _opimpl_setfield_raw_any
 
-    @arguments("box", "descr", "descr")
+    @arguments("box", "descr", "descr", "orgpc")
     def opimpl_record_quasiimmut_field(self, box, fielddescr,
-                                       mutatefielddescr):
+                                       mutatefielddescr, orgpc):
         from pypy.jit.metainterp.quasiimmut import QuasiImmutDescr
         cpu = self.metainterp.cpu
         descr = QuasiImmutDescr(cpu, box, fielddescr, mutatefielddescr)
         self.metainterp.history.record(rop.QUASIIMMUT_FIELD, [box],
                                        None, descr=descr)
+        self.generate_guard(rop.GUARD_NOT_INVALIDATED, resumepc=orgpc)
 
     def _nonstandard_virtualizable(self, pc, box):
         # returns True if 'box' is actually not the "standard" virtualizable
@@ -1085,6 +1086,8 @@
         if opnum == rop.GUARD_NOT_FORCED:
             resumedescr = compile.ResumeGuardForcedDescr(metainterp_sd,
                                                    metainterp.jitdriver_sd)
+        elif opnum == rop.GUARD_NOT_INVALIDATED:
+            resumedescr = compile.ResumeGuardNotInvalidated()
         else:
             resumedescr = compile.ResumeGuardDescr()
         guard_op = metainterp.history.record(opnum, moreargs, None,
@@ -1857,6 +1860,9 @@
                 self.handle_possible_exception()
             except ChangeFrame:
                 pass
+        elif opnum == rop.GUARD_NOT_INVALIDATED:
+            pass # XXX we want to do something special in resume descr,
+                 # but not now
         elif opnum == rop.GUARD_NO_OVERFLOW:   # an overflow now detected
             self.execute_raised(OverflowError(), constant=True)
             try:

diff --git a/pypy/jit/codewriter/test/test_jtransform.py b/pypy/jit/codewriter/test/test_jtransform.py
--- a/pypy/jit/codewriter/test/test_jtransform.py
+++ b/pypy/jit/codewriter/test/test_jtransform.py
@@ -960,7 +960,7 @@
         op = SpaceOperation('getfield', [v_x, Constant('inst_x', lltype.Void)],
                             v2)
         tr = Transformer(FakeCPU())
-        [op1, op2] = tr.rewrite_operation(op)
+        [_, op1, op2] = tr.rewrite_operation(op)
         assert op1.opname == 'record_quasiimmut_field'
         assert len(op1.args) == 3
         assert op1.args[0] == v_x

diff --git a/pypy/jit/metainterp/test/test_quasiimmut.py b/pypy/jit/metainterp/test/test_quasiimmut.py
--- a/pypy/jit/metainterp/test/test_quasiimmut.py
+++ b/pypy/jit/metainterp/test/test_quasiimmut.py
@@ -1,3 +1,6 @@
+
+import py
+
 from pypy.rpython.lltypesystem import lltype, llmemory, rclass
 from pypy.rpython.rclass import FieldListAccessor, IR_QUASI_IMMUTABLE
 from pypy.jit.metainterp import typesystem

diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -119,6 +119,7 @@
         self._lazy_setfields = []
         # cached array items:  {descr: CachedArrayItems}
         self.cached_arrayitems = {}
+        self._remove_guard_not_invalidated = False
 
     def reconstruct_for_next_iteration(self, optimizer, valuemap):
         new = OptHeap()
@@ -386,6 +387,7 @@
         # constant-fold the following getfield_gc.
         structvalue = self.getvalue(op.getarg(0))
         if not structvalue.is_constant():
+            self._remove_guard_not_invalidated = True
             return    # not a constant at all; ignore QUASIIMMUT_FIELD
         #
         from pypy.jit.metainterp.quasiimmut import QuasiImmutDescr
@@ -396,6 +398,7 @@
         # simply ignoring the QUASIIMMUT_FIELD hint and compiling it
         # as a regular getfield.
         if not qmutdescr.is_still_valid():
+            self._remove_guard_not_invalidated = True
             return
         # record as an out-of-line guard
         if self.optimizer.quasi_immutable_deps is None:
@@ -405,6 +408,13 @@
         fieldvalue = self.getvalue(qmutdescr.constantfieldbox)
         cf = self.field_cache(qmutdescr.fielddescr)
         cf.remember_field_value(structvalue, fieldvalue)
+        self._remove_guard_not_invalidated = False
+
+    def optimize_GUARD_NOT_INVALIDATED(self, op):
+        if self._remove_guard_not_invalidated:
+            return
+        self._remove_guard_not_invalidated = False
+        self.emit_operation(op)
 
     def propagate_forward(self, op):
         opnum = op.getopnum()

diff --git a/pypy/jit/metainterp/quasiimmut.py b/pypy/jit/metainterp/quasiimmut.py
--- a/pypy/jit/metainterp/quasiimmut.py
+++ b/pypy/jit/metainterp/quasiimmut.py
@@ -81,9 +81,7 @@
         for wref in wrefs:
             looptoken = wref()
             if looptoken is not None:
-                pass
-                #
-                # XXX tell the backend to mark the loop as invalid
+                self.cpu.invalidate_loop(looptoken)
 
 
 class QuasiImmutDescr(AbstractDescr):

diff --git a/pypy/jit/metainterp/blackhole.py b/pypy/jit/metainterp/blackhole.py
--- a/pypy/jit/metainterp/blackhole.py
+++ b/pypy/jit/metainterp/blackhole.py
@@ -1292,6 +1292,8 @@
             # We get here because it used to overflow, but now it no longer
             # does.
             pass
+        elif opnum == rop.GUARD_NOT_INVALIDATED:
+            pass
         else:
             from pypy.jit.metainterp.resoperation import opname
             raise NotImplementedError(opname[opnum])

diff --git a/pypy/jit/codewriter/jtransform.py b/pypy/jit/codewriter/jtransform.py
--- a/pypy/jit/codewriter/jtransform.py
+++ b/pypy/jit/codewriter/jtransform.py
@@ -583,7 +583,8 @@
             descr1 = self.cpu.fielddescrof(
                 v_inst.concretetype.TO,
                 quasiimmut.get_mutate_field_name(c_fieldname.value))
-            op1 = [SpaceOperation('record_quasiimmut_field',
+            op1 = [SpaceOperation('-live-', [], None),
+                   SpaceOperation('record_quasiimmut_field',
                                   [v_inst, descr, descr1], None),
                    op1]
         return op1

diff --git a/pypy/jit/backend/llgraph/llimpl.py b/pypy/jit/backend/llgraph/llimpl.py
--- a/pypy/jit/backend/llgraph/llimpl.py
+++ b/pypy/jit/backend/llgraph/llimpl.py
@@ -167,6 +167,7 @@
 
 class CompiledLoop(object):
     has_been_freed = False
+    invalid = False
 
     def __init__(self):
         self.inputargs = []
@@ -933,6 +934,9 @@
         if forced:
             raise GuardFailed
 
+    def op_guard_not_invalidated(self, descr):
+        if self.loop.invalid:
+            raise GuardFailed
 
 class OOFrame(Frame):
 

diff --git a/pypy/jit/metainterp/test/test_optimizeopt.py b/pypy/jit/metainterp/test/test_optimizeopt.py
--- a/pypy/jit/metainterp/test/test_optimizeopt.py
+++ b/pypy/jit/metainterp/test/test_optimizeopt.py
@@ -5702,8 +5702,35 @@
         # not obvious, because of the exception UnicodeDecodeError that
         # can be raised by ll_str2unicode()
 
-
-
+    def test_quasi_immut(self):
+        ops = """
+        [p0, p1, i0]
+        quasiimmut_field(p0, descr=quasiimmutdescr)
+        guard_not_invalidated() []
+        i1 = getfield_gc(p0, descr=quasifielddescr)
+        jump(p1, p0, i1)
+        """
+        expected = """
+        [p0, p1, i0]
+        i1 = getfield_gc(p0, descr=quasifielddescr)
+        jump(p1, p0, i1)
+        """
+        self.optimize_loop(ops, expected)
+
+    def test_quasi_immut_2(self):
+        ops = """
+        []
+        quasiimmut_field(ConstPtr(myptr), descr=quasiimmutdescr)
+        guard_not_invalidated() []
+        i1 = getfield_gc(ConstPtr(myptr), descr=quasifielddescr)
+        jump()
+        """
+        expected = """
+        []
+        guard_not_invalidated() []        
+        jump()
+        """
+        self.optimize_loop(ops, expected, expected)
 
 ##class TestOOtype(OptimizeOptTest, OOtypeMixin):
 

diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -380,6 +380,7 @@
     'GUARD_NO_OVERFLOW/0d',
     'GUARD_OVERFLOW/0d',
     'GUARD_NOT_FORCED/0d',
+    'GUARD_NOT_INVALIDATED/0d',
     '_GUARD_LAST', # ----- end of guard operations -----
 
     '_NOSIDEEFFECT_FIRST', # ----- start of no_side_effect operations -----


More information about the Pypy-commit mailing list