Author: Armin Rigo <ar...@tunes.org>
Branch: stmgc-static-barrier
Changeset: r66288:7a6758a0e339
Date: 2013-08-22 16:56 +0200
http://bitbucket.org/pypy/pypy/changeset/7a6758a0e339/

Log:    Do some whole-graph analysis.

diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -624,6 +624,7 @@
     'debug_reraise_traceback': LLOp(),
     'debug_print_traceback':   LLOp(),
     'debug_nonnull_pointer':   LLOp(canrun=True),
+    'debug_stm_flush_barrier': LLOp(canrun=True),
 
     # __________ instrumentation _________
     'instrument_count':     LLOp(),
diff --git a/rpython/rtyper/lltypesystem/opimpl.py 
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -673,6 +673,9 @@
 def op_nop(x):
     pass
 
+def op_debug_stm_flush_barrier():
+    pass
+
 # ____________________________________________________________
 
 def get_op_impl(opname):
diff --git a/rpython/translator/stm/test/test_writebarrier.py 
b/rpython/translator/stm/test/test_writebarrier.py
--- a/rpython/translator/stm/test/test_writebarrier.py
+++ b/rpython/translator/stm/test/test_writebarrier.py
@@ -1,5 +1,6 @@
 from rpython.rlib.rstm import register_invoke_around_extcall
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
+from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.translator.stm.test.transform_support import BaseTestTransform
 
 
@@ -294,16 +295,15 @@
                 x = Z()
                 x.foo = 815
                 x.zbar = 'A'
-            external_any_gcobj()
+            llop.debug_stm_flush_barrier(lltype.Void)
             result = x.foo          # 1
             if isinstance(x, Y):    # 2
-                result += x.ybar    # 3
+                result += x.ybar    # 3: optimized
             return result
 
         res = self.interpret(f1, [10])
         assert res == 42 + 10
-        assert self.barriers == ['a2r', 'a2i', 'a2r'] # from 3 blocks (could be
-                                                      # optimized later)
+        assert self.barriers == ['a2r', 'a2i']
         res = self.interpret(f1, [-10])
         assert res == 815
         assert self.barriers == ['a2r', 'a2i']
@@ -318,7 +318,7 @@
             return y
         def f1(i):
             y = make_y(i)
-            external_any_gcobj()
+            llop.debug_stm_flush_barrier(lltype.Void)
             prev = y.ybar          # a2r
             handle(y)              # inside handle(): a2r, r2v
             return prev + y.ybar   # q2r
@@ -343,7 +343,7 @@
             else:
                 x = Z(); x.foo = 815; x.zbar = 'A'
                 y = Y(); y.foo = -13; y.ybar = i
-            external_any_gcobj()
+            llop.debug_stm_flush_barrier(lltype.Void)
             prev = x.foo           # a2r
             handle(y)              # inside handle(): a2r, r2v
             return prev + x.foo    # q2r
@@ -366,7 +366,7 @@
             else:
                 y = lltype.nullptr(Y)
                 x = lltype.cast_opaque_ptr(llmemory.GCREF, y)
-            external_any_gcobj()
+            llop.debug_stm_flush_barrier(lltype.Void)
             prev = lltype.cast_opaque_ptr(YPTR, x).foo           # a2r
             handle(y)                            # inside handle(): a2r, r2v
             return prev + lltype.cast_opaque_ptr(YPTR, x).ybar   # q2r?
@@ -387,7 +387,7 @@
         def f1(i):
             x.a = x2  # write barrier
             y = X()   # malloc
-            x.a = x3  # write barrier again
+            x.a = x3  # repeat write barrier
             return y
 
         res = self.interpret(f1, [10])
@@ -399,8 +399,10 @@
 
         def f1(n):
             x = Foo()
+            llop.debug_stm_flush_barrier(lltype.Void)
             if n > 1:
                 x.foo = n
+            llop.debug_stm_flush_barrier(lltype.Void)
             return x.foo
 
         res = self.interpret(f1, [4])
diff --git a/rpython/translator/stm/test/transform_support.py 
b/rpython/translator/stm/test/transform_support.py
--- a/rpython/translator/stm/test/transform_support.py
+++ b/rpython/translator/stm/test/transform_support.py
@@ -77,7 +77,7 @@
 
     def check_category(self, p, expected):
         cat = self.get_category_or_null(p)
-        assert cat in 'AIQRVW' or cat is None
+        assert cat is None or cat in 'AIQRVW'
         if expected is not None:
             assert cat is not None and cat >= expected
         return cat
diff --git a/rpython/translator/stm/writebarrier.py 
b/rpython/translator/stm/writebarrier.py
--- a/rpython/translator/stm/writebarrier.py
+++ b/rpython/translator/stm/writebarrier.py
@@ -1,6 +1,6 @@
 from rpython.flowspace.model import SpaceOperation, Constant, Variable
-from rpython.flowspace.model import mkentrymap
 from rpython.translator.unsimplify import varoftype, insert_empty_block
+from rpython.translator.unsimplify import insert_empty_startblock
 from rpython.rtyper.lltypesystem import lltype
 from rpython.translator.backendopt.writeanalyze import top_set
 
@@ -38,15 +38,21 @@
     return to > frm
 
 
+class Renaming(object):
+    def __init__(self, newvar, category):
+        self.newvar = newvar        # a Variable or a Constant
+        self.TYPE = newvar.concretetype
+        self.category = category
+
+
 class BlockTransformer(object):
 
-    def __init__(self, stmtransformer, block, entrylinks):
+    def __init__(self, stmtransformer, block):
         self.stmtransformer = stmtransformer
         self.block = block
-        self.inputargs_category = {}
         self.patch = None
-        for link in entrylinks:
-            self.inputargs_category[link] = ['A'] * len(link.args)
+        self.inputargs_category = [None] * len(block.inputargs)
+        self.inputargs_category_per_link = {}
 
 
     def analyze_inside_block(self):
@@ -98,54 +104,71 @@
         #
         self.wants_a_barrier = wants_a_barrier
         self.expand_comparison = expand_comparison
-        return bool(wants_a_barrier or expand_comparison)
 
 
     def flow_through_block(self, graphinfo):
 
-        def get_category(v):
-            if isinstance(v, Constant):
-                default = 'I'    # prebuilt objects cannot be stubs
-            else:
-                default = 'A'
-            return category.get(v, default)
+        def renfetch(v):
+            try:
+                return renamings[v]
+            except KeyError:
+                if isinstance(v, Variable):
+                    ren = Renaming(v, 'A')
+                else:
+                    ren = Renaming(v, 'I')  # prebuilt objects cannot be stubs
+                renamings[v] = ren
+                return ren
 
         def get_category_or_null(v):
-            if isinstance(v, Constant) and not v.value:
+            # 'v' is an original variable here, or a constant
+            if isinstance(v, Constant) and not v.value:    # a NULL constant
                 return None
-            return category.get(v, 'A')
+            if v in renamings:
+                return renamings[v].category
+            if isinstance(v, Constant):
+                return 'I'
+            else:
+                return 'A'
 
         def renamings_get(v):
-            if v not in renamings:
-                return v
-            v2 = renamings[v][0]
+            try:
+                ren = renamings[v]
+            except KeyError:
+                return v       # unmodified
+            v2 = ren.newvar
             if v2.concretetype == v.concretetype:
                 return v2
             v3 = varoftype(v.concretetype)
             newoperations.append(SpaceOperation('cast_pointer', [v2], v3))
+            if lltype.castable(ren.TYPE, v3.concretetype) > 0:
+                ren.TYPE = v3.concretetype
             return v3
 
         # note: 'renamings' maps old vars to new vars, but cast_pointers
         # are done lazily.  It means that the two vars may not have
         # exactly the same type.
-        renamings = {}   # {original-var: [var-in-newoperations] (len 1)}
-        category = {}    # {var-in-newoperations: LETTER}
+        renamings = {}   # {original-var: Renaming(newvar, category)}
         newoperations = []
         stmtransformer = self.stmtransformer
 
+        # make the initial trivial renamings needed to have some precise
+        # categories for the input args
+        for v, cat in zip(self.block.inputargs, self.inputargs_category):
+            if (cat is not None and
+                    isinstance(v.concretetype, lltype.Ptr) and
+                    v.concretetype.TO._gckind == 'gc'):
+                renamings[v] = Renaming(v, cat)
+
         for op in self.block.operations:
             #
-            if op.opname == 'cast_pointer':
-                v = op.args[0]
-                renamings[op.result] = renamings.setdefault(v, [v])
+            if op.opname in ('cast_pointer', 'same_as'):
+                renamings[op.result] = renfetch(op.args[0])
                 continue
             #
             to = self.wants_a_barrier.get(op)
             if to is not None:
-                v = op.args[0]
-                v_holder = renamings.setdefault(v, [v])
-                v = v_holder[0]
-                frm = get_category(v)
+                ren = renfetch(op.args[0])
+                frm = ren.category
                 if needs_barrier(frm, to):
                     try:
                         b = stmtransformer.barrier_counts[frm, to]
@@ -155,11 +178,12 @@
                         stmtransformer.barrier_counts[frm, to] = b
                     b[0] += 1
                     c_info = b[1]
+                    v = ren.newvar
                     w = varoftype(v.concretetype)
                     newop = SpaceOperation('stm_barrier', [c_info, v], w)
                     newoperations.append(newop)
-                    v_holder[0] = w
-                    category[w] = to
+                    ren.newvar = w
+                    ren.category = to
             #
             newop = SpaceOperation(op.opname,
                                    [renamings_get(v) for v in op.args],
@@ -167,8 +191,8 @@
             newoperations.append(newop)
             #
             if op in self.expand_comparison:
-                cats = (get_category_or_null(newop.args[0]),
-                        get_category_or_null(newop.args[1]))
+                cats = (get_category_or_null(op.args[0]),
+                        get_category_or_null(op.args[1]))
                 if None not in cats and (cats[0] < 'V' or cats[1] < 'V'):
                     if newop.opname == 'ptr_ne':
                         v = varoftype(lltype.Bool)
@@ -183,17 +207,21 @@
                 # all pointers are lowered to 'I', because a non-
                 # stub cannot suddenly point to a stub, but we
                 # cannot guarantee anything more
-                for v, cat in category.items():
-                    if cat > 'I':
-                        category[v] = 'I'
+                for ren in renamings.values():
+                    if ren.category > 'I':
+                        ren.category = 'I'
+
+            if op.opname == 'debug_stm_flush_barrier':
+                for ren in renamings.values():
+                    ren.category = 'A'
 
             if stmtransformer.collect_analyzer.analyze(op):
                 # this operation can collect: we bring all 'W'
                 # categories back to 'V', because we would need
                 # a repeat_write_barrier on them afterwards
-                for v, cat in category.items():
-                    if cat == 'W':
-                        category[v] = 'V'
+                for ren in renamings.values():
+                    if ren.category == 'W':
+                        ren.category = 'V'
 
             effectinfo = stmtransformer.write_analyzer.analyze(
                 op, graphinfo=graphinfo)
@@ -202,9 +230,9 @@
                     # this operation can perform random writes: any
                     # 'R'-category object falls back to 'Q' because
                     # we would need a repeat_read_barrier()
-                    for v, cat in category.items():
-                        if cat == 'R':
-                            category[v] = 'Q'
+                    for ren in renamings.values():
+                        if ren.category == 'R':
+                            ren.category = 'Q'
                 else:
                     # the same, but only on objects of the right types
                     # -- we need to consider 'types' or any base type
@@ -216,34 +244,82 @@
                             if not isinstance(TYPE, lltype.Struct):
                                 break
                             _, TYPE = TYPE._first_struct()
-                    for v in category.keys():
-                        if (v.concretetype.TO in types and
-                               category[v] == 'R'):
-                            category[v] = 'Q'
+                    for ren in renamings.values():
+                        if ren.TYPE.TO in types and ren.category == 'R':
+                            ren.category = 'Q'
 
             if op.opname in MALLOCS:
-                category[op.result] = 'W'
+                assert op.result not in renamings
+                renamings[op.result] = Renaming(op.result, 'W')
 
+        if isinstance(self.block.exitswitch, Variable):
+            switchv = renamings_get(self.block.exitswitch)
+        else:
+            switchv = None
         blockoperations = newoperations
         linkoperations = []
         for link in self.block.exits:
+            output_categories = []
+            for v in link.args:
+                if (isinstance(v.concretetype, lltype.Ptr) and
+                        v.concretetype.TO._gckind == 'gc'):
+                    cat = get_category_or_null(v)
+                else:
+                    cat = None
+                output_categories.append(cat)
             newoperations = []
             newargs = [renamings_get(v) for v in link.args]
-            linkoperations.append((newargs, newoperations))
+            linkoperations.append((newargs, newoperations, output_categories))
         #
         # Record how we'd like to patch the block, but don't do any
         # patching yet
-        self.patch = (blockoperations, linkoperations)
+        self.patch = (blockoperations, switchv, linkoperations)
+
+
+    def update_targets(self, block_transformers):
+        (_, _, linkoperations) = self.patch
+        assert len(linkoperations) == len(self.block.exits)
+        targetbts = []
+        for link, (_, _, output_categories) in zip(self.block.exits,
+                                                   linkoperations):
+            targetblock = link.target
+            if targetblock not in block_transformers:
+                continue      # ignore the exit block
+            targetbt = block_transformers[targetblock]
+            targetbt.inputargs_category_per_link[link] = output_categories
+            if targetbt.update_inputargs_category():
+                targetbts.append(targetbt)
+        return set(targetbts)
+
+    def update_inputargs_category(self):
+        values = self.inputargs_category_per_link.values()
+        newcats = []
+        for i in range(len(self.block.inputargs)):
+            cat = None
+            for output_categories in values:
+                cat2 = output_categories[i]
+                if cat is None:
+                    cat = cat2
+                elif cat2 is not None:
+                    cat = min(cat, cat2)
+            newcats.append(cat)
+        if newcats != self.inputargs_category:
+            self.inputargs_category = newcats
+            return True
+        else:
+            return False
 
 
     def patch_now(self):
         if self.patch is None:
             return
-        newoperations, linkoperations = self.patch
+        newoperations, switchv, linkoperations = self.patch
         self.block.operations = newoperations
+        if switchv is not None:
+            self.block.exitswitch = switchv
         assert len(linkoperations) == len(self.block.exits)
-        for link, (newargs, newoperations) in zip(self.block.exits,
-                                                  linkoperations):
+        for link, (newargs, newoperations, _) in zip(self.block.exits,
+                                                     linkoperations):
             link.args[:] = newargs
             if newoperations:
                 # must put them in a fresh block along the link
@@ -266,22 +342,24 @@
        pointer from category x to category y if and only if y > x.
     """
     graphinfo = stmtransformer.write_analyzer.compute_graph_info(graph)
+    annotator = stmtransformer.translator.annotator
+    insert_empty_startblock(annotator, graph)
 
     block_transformers = {}
-    entrymap = mkentrymap(graph)
     pending = set()
 
     for block in graph.iterblocks():
         if block.operations == ():
             continue
-        bt = BlockTransformer(stmtransformer, block, entrymap[block])
-        if bt.analyze_inside_block():
-            pending.add(bt)
+        bt = BlockTransformer(stmtransformer, block)
+        bt.analyze_inside_block()
         block_transformers[block] = bt
+        pending.add(bt)
 
     while pending:
         bt = pending.pop()
         bt.flow_through_block(graphinfo)
+        pending |= bt.update_targets(block_transformers)
 
     for bt in block_transformers.values():
         bt.patch_now()
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to