Author: Remi Meier <[email protected]>
Branch: stmgc-c8
Changeset: r80888:6882bd53ee96
Date: 2015-11-24 15:55 +0100
http://bitbucket.org/pypy/pypy/changeset/6882bd53ee96/

Log:    add analysis for better write barrier placement

        Adds a class that looks for write/set-operations that do not require
        a write barrier since the target already had a barrier executed on
        all paths to these operations or the target is freshly allocated
        (with no can-collect inbetween).

diff --git a/rpython/memory/gctransform/framework.py 
b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -52,6 +52,147 @@
             return (op.opname in LL_OPERATIONS and
                     LL_OPERATIONS[op.opname].canmallocgc)
 
+
+class WriteBarrierCollector(object):
+    """
+    This class implements a forward data flow analysis to find all
+    set/write-operations in the graph that do *not* require a
+    barrier, either because they are freshly allocated or they
+    just had a barrier-requiring operation before them.
+    """
+
+    def __init__(self, graph, collect_analyzer):
+        self.graph = graph
+        self.collect_analyzer = collect_analyzer
+        self.clean_ops = set()
+        self._in_states = {} # block->state of each inputarg
+        self._out_states = {} # block->writable dict
+        self.in_stm_ignored = False
+
+    def _merge_out_states(self, out_states):
+        assert out_states
+        # True: writeable or fresh, otherwise False
+        result = [True] * len(out_states[0])
+        for outset in out_states:
+            for i, state in enumerate(outset):
+                result[i] = result[i] and state
+        return result
+
+    def _set_into_gc_array_part(self, op):
+        if op.opname == 'setarrayitem':
+            return op.args[1]
+        if op.opname == 'setinteriorfield':
+            for v in op.args[1:-1]:
+                if v.concretetype is not lltype.Void:
+                    return v
+        return None
+
+    def _flow_through_block(self, block):
+        # get all out_state of predecessors and recalculate
+        # the in_state of our block (except for startblock)
+        insets = self._in_states
+        #
+        # get input variables and their states:
+        writeable = {}
+        for v, state in zip(block.inputargs, insets[block]):
+            writeable[v] = state
+        #
+        # flow through block and inspect operations that
+        # need a write barrier, and those that create new
+        # objects
+        for i, op in enumerate(block.operations):
+            if self.collect_analyzer.analyze(op): # incl. malloc, obviously
+                # clear all writeable status
+                writeable = {}
+            #
+            if op.opname == "stm_ignored_start":
+                self.in_stm_ignored = True
+            elif op.opname == "stm_ignored_stop":
+                self.in_stm_ignored = False
+            elif op.opname == "malloc":
+                rtti = get_rtti(op.args[0].value)
+                if rtti is not None and hasattr(rtti._obj, 
'destructor_funcptr'):
+                    # XXX: not sure why that matters, copied from
+                    # find_initializing_stores
+                    continue
+                writeable[op.result] = True
+                #
+            elif op.opname in ("cast_pointer", "same_as"):
+                if writeable.get(op.args[0], False):
+                    writeable[op.result] = True
+                #
+            elif op.opname in ('setfield', 'setarrayitem', 'setinteriorfield', 
'raw_store'):
+                if op.args[-1].concretetype == lltype.Void:
+                    self.clean_ops.add(op)
+                    continue # ignore setfields of Void type
+                elif not var_needsgc(op.args[0]):
+                    if (var_needsgc(op.args[-1]) and
+                        'is_excdata' not in op.args[0].concretetype.TO._hints):
+                        raise Exception("%s: GC pointer written into a non-GC 
location"
+                                        % (op,))
+                    self.clean_ops.add(op)
+                    continue
+                elif self.in_stm_ignored:
+                    # detect if we're inside a 'stm_ignored' block and in
+                    # that case don't call stm_write().  This only works for
+                    # writing non-GC pointers.
+                    if var_needsgc(op.args[-1]):
+                        raise Exception("in stm_ignored block: write of a gc 
pointer")
+                    self.clean_ops.add(op)
+                    continue
+                elif self._set_into_gc_array_part(op) is None:
+                    # full write barrier required
+                    if writeable.get(op.args[0], False):
+                        # already writeable, this op is also clean
+                        self.clean_ops.add(op)
+                    elif op in self.clean_ops:
+                        # we require an stm_write
+                        self.clean_ops.remove(op)
+                    # always writeable after this op
+                    writeable[op.args[0]] = True
+                else:
+                    # things that need partial write barriers (card marking)
+                    assert op not in self.clean_ops
+        #
+        # update in_states of all successors
+        updated = set()
+        for link in block.exits:
+            succ = link.target
+            outset = [writeable.get(v, False) for v in link.args]
+            if succ in insets:
+                to_merge = [insets[succ], outset]
+                new = self._merge_out_states(to_merge)
+                if new != insets[succ]:
+                    updated.add(succ)
+                    insets[succ] = new
+            else:
+                # block not processed yet
+                insets[succ] = outset
+                updated.add(succ)
+        return updated
+
+
+    def collect(self):
+        assert not self.clean_ops
+        assert not self.in_stm_ignored
+        # we implement a forward-flow analysis that determines the
+        # operations that do NOT need a write barrier
+        graph = self.graph
+        #
+        # initialize blocks
+        self._in_states = {}
+        self._in_states[graph.startblock] = [False] * 
len(graph.startblock.inputargs)
+        #
+        # fixpoint iteration
+        # XXX: reverse postorder traversal
+        pending = {graph.startblock,}
+        while pending:
+            block = pending.pop()
+            pending |= self._flow_through_block(block)
+
+
+
+
 def propagate_no_write_barrier_needed(result, block, mallocvars,
                                       collect_analyzer, entrymap,
                                       startindex=0):
@@ -730,9 +871,14 @@
         if self.write_barrier_ptr:
             from rpython.flowspace.model import mkentrymap
             self._entrymap = mkentrymap(graph)
-            self.clean_sets = (
-                find_initializing_stores(self.collect_analyzer, graph,
-                                         self._entrymap))
+            if self.translator.config.translation.stm:
+                wbc = WriteBarrierCollector(graph, self.collect_analyzer)
+                wbc.collect()
+                self.clean_sets = wbc.clean_ops
+            else:
+                self.clean_sets = (
+                    find_initializing_stores(self.collect_analyzer, graph,
+                                             self._entrymap))
             if self.gcdata.gc.can_optimize_clean_setarrayitems():
                 self.clean_sets = self.clean_sets.union(
                     find_clean_setarrayitems(self.collect_analyzer, graph))
diff --git a/rpython/memory/gctransform/test/test_framework.py 
b/rpython/memory/gctransform/test/test_framework.py
--- a/rpython/memory/gctransform/test/test_framework.py
+++ b/rpython/memory/gctransform/test/test_framework.py
@@ -4,8 +4,9 @@
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.memory.gc.semispace import SemiSpaceGC
-from rpython.memory.gctransform.framework import (CollectAnalyzer,
-     find_initializing_stores, find_clean_setarrayitems)
+from rpython.memory.gctransform.framework import (
+    CollectAnalyzer, WriteBarrierCollector,
+    find_initializing_stores, find_clean_setarrayitems)
 from rpython.memory.gctransform.shadowstack import (
      ShadowStackFrameworkGCTransformer)
 from rpython.memory.gctransform.test.test_transform import rtype
@@ -253,7 +254,7 @@
          Constant('b', lltype.Void), varoftype(PTR_TYPE2)],
         varoftype(lltype.Void)))
 
-def test_remove_duplicate_write_barrier():
+def test_remove_duplicate_write_barrier(gc="minimark"):
     from rpython.translator.c.genc import CStandaloneBuilder
     from rpython.flowspace.model import summary
 
@@ -272,17 +273,59 @@
         f(glob_a_2, 0)
         return 0
     t = rtype(g, [s_list_of_strings])
-    t.config.translation.gc = "minimark"
+    if gc == "stmgc":
+        t.config.translation.stm = True
+        gcpolicy = StmFrameworkGcPolicy
+    else:
+        gcpolicy = FrameworkGcPolicy2
+    t.config.translation.gc = gc
     cbuild = CStandaloneBuilder(t, g, t.config,
-                                gcpolicy=FrameworkGcPolicy2)
+                                gcpolicy=gcpolicy)
     db = cbuild.generate_graphs_for_llinterp()
 
     ff = graphof(t, f)
     #ff.show()
-    assert summary(ff)['direct_call'] == 1    # only one remember_young_pointer
+    if gc == "stmgc":
+        assert summary(ff)['stm_write'] == 1
+    else:
+        assert summary(ff)['direct_call'] == 1    # only one 
remember_young_pointer
 
-def test_find_initializing_stores():
+def test_remove_duplicate_write_barrier_stm():
+    test_remove_duplicate_write_barrier("stmgc")
 
+def test_remove_write_barrier_stm():
+    from rpython.translator.c.genc import CStandaloneBuilder
+    from rpython.flowspace.model import summary
+
+    class A: pass
+    glob = A()
+    glob2 = A()
+    def f(n, g):
+        i = 0
+        g.a = n
+        while i < n:
+            g.a = i
+            i += 1
+    def g(argv):
+        if argv[1]:
+            f(int(argv[1]), glob)
+        else:
+            f(int(argv[1]), glob2)
+        return 0
+
+    t = rtype(g, [s_list_of_strings])
+    t.config.translation.stm = True
+    gcpolicy = StmFrameworkGcPolicy
+    t.config.translation.gc = "stmgc"
+    cbuild = CStandaloneBuilder(t, g, t.config,
+                                gcpolicy=gcpolicy)
+    db = cbuild.generate_graphs_for_llinterp()
+
+    ff = graphof(t, f)
+    ff.show()
+    assert summary(ff)['stm_write'] == 1    # only one remember_young_pointer
+
+def test_write_barrier_collector():
     class A(object):
         pass
     class B(object):
@@ -293,15 +336,19 @@
         b.a = a
         b.b = 1
     t = rtype(f, [])
+    t.config.translation.stm = True
+    #gcpolicy = StmFrameworkGcPolicy
+    t.config.translation.gc = "stmgc"
     etrafo = ExceptionTransformer(t)
     graphs = etrafo.transform_completely()
     collect_analyzer = CollectAnalyzer(t)
-    init_stores = find_initializing_stores(collect_analyzer, t.graphs[0],
-                                           mkentrymap(t.graphs[0]))
-    assert len(init_stores) == 4
 
-def test_find_initializing_stores_across_blocks():
+    wbc = WriteBarrierCollector(t.graphs[0], collect_analyzer)
+    wbc.collect()
+    print wbc.clean_ops
+    assert len(wbc.clean_ops) == 4
 
+def test_write_barrier_collector_across_blocks():
     class A(object):
         pass
     class B(object):
@@ -319,12 +366,82 @@
             b.c = a1
             b.b = a2
     t = rtype(f, [int])
+    t.config.translation.stm = True
+    #gcpolicy = StmFrameworkGcPolicy
+    t.config.translation.gc = "stmgc"
     etrafo = ExceptionTransformer(t)
     graphs = etrafo.transform_completely()
     collect_analyzer = CollectAnalyzer(t)
-    init_stores = find_initializing_stores(collect_analyzer, t.graphs[0],
-                                           mkentrymap(t.graphs[0]))
-    assert len(init_stores) == 9
+
+    wbc = WriteBarrierCollector(t.graphs[0], collect_analyzer)
+    wbc.collect()
+    print "\n".join(map(str,wbc.clean_ops))
+    assert len(wbc.clean_ops) == 9
+
+def test_write_barrier_collector_blocks_merging():
+    class A(object):
+        pass
+    class B(object):
+        pass
+    def f(x):
+        a1 = A()
+        a2 = A()
+        a = A()
+        b = B()
+        b.a = a
+        if x:
+            b.b = a1
+            b.c = a2
+        else:
+            b.c = a1
+            b.b = a2
+            a.c = a2 # WB
+        b.c = a1
+        a.c = a1 # WB
+        a.b = a2
+    t = rtype(f, [int])
+    t.config.translation.stm = True
+    #gcpolicy = StmFrameworkGcPolicy
+    t.config.translation.gc = "stmgc"
+    etrafo = ExceptionTransformer(t)
+    graphs = etrafo.transform_completely()
+    collect_analyzer = CollectAnalyzer(t)
+
+    wbc = WriteBarrierCollector(t.graphs[0], collect_analyzer)
+    wbc.collect()
+    print "\n".join(map(str,wbc.clean_ops))
+    assert len(wbc.clean_ops) == 11
+
+def test_write_barrier_collector_loops():
+    class A(object):
+        pass
+    class B(object):
+        pass
+    def f(x):
+        a1 = A()
+        a2 = A()
+        a = A()
+        b = B()
+        b.a = a
+        while x:
+            b.b = a1
+            a.c = a2
+        b.c = a1
+        a.c = a1
+    t = rtype(f, [int])
+    t.config.translation.stm = True
+    #gcpolicy = StmFrameworkGcPolicy
+    t.config.translation.gc = "stmgc"
+    etrafo = ExceptionTransformer(t)
+    graphs = etrafo.transform_completely()
+    collect_analyzer = CollectAnalyzer(t)
+
+    wbc = WriteBarrierCollector(t.graphs[0], collect_analyzer)
+    wbc.collect()
+    print "\n".join(map(str,wbc.clean_ops))
+    assert len(wbc.clean_ops) == 7
+
+
 
 def test_find_clean_setarrayitems():
     S = lltype.GcStruct('S')
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to