[pypy-commit] pypy stmgc-c8: Plug the logic from find_initializing_stores() to also detect and remove

arigo noreply at buildbot.pypy.org
Thu Jun 25 22:47:32 CEST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: stmgc-c8
Changeset: r78311:8043ab320b22
Date: 2015-06-25 11:11 +0200
http://bitbucket.org/pypy/pypy/changeset/8043ab320b22/

Log:	Plug the logic from find_initializing_stores() to also detect and
	remove duplicate write barrier calls done without any malloc()

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,23 +52,19 @@
             return (op.opname in LL_OPERATIONS and
                     LL_OPERATIONS[op.opname].canmallocgc)
 
-
-
-def find_initializing_stores(collect_analyzer, graph):
-    from rpython.flowspace.model import mkentrymap
-    entrymap = mkentrymap(graph)
-    # a bit of a hackish analysis: if a block contains a malloc and check that
-    # the result is not zero, then the block following the True link will
-    # usually initialize the newly allocated object
-    result = set()
-    def find_in_block(block, mallocvars):
+def propagate_no_write_barrier_needed(result, block, mallocvars,
+                                      collect_analyzer, entrymap):
+    # We definitely know that no write barrier is needed in the 'block'
+    # for any of the variables in 'mallocvars'.  Propagate this information
+    # forward.  Note that "definitely know" implies that we just did either
+    # a fixed-size malloc (variable-size might require card marking), or
+    # that we just did a full write barrier (not just for card marking).
+    if 1:       # keep indentation
         for i, op in enumerate(block.operations):
             if op.opname in ("cast_pointer", "same_as"):
                 if op.args[0] in mallocvars:
                     mallocvars[op.result] = True
             elif op.opname in ("setfield", "setarrayitem", "setinteriorfield"):
-                # note that 'mallocvars' only tracks fixed-size mallocs,
-                # so no risk that they use card marking
                 if op.args[0] in mallocvars:
                     # Collect all assignments, even if they are not storing
                     # a pointer.  This is needed for stmframework and doesn't
@@ -85,7 +81,15 @@
                 if var in mallocvars:
                     newmallocvars[exit.target.inputargs[i]] = True
             if newmallocvars:
-                find_in_block(exit.target, newmallocvars)
+                propagate_no_write_barrier_needed(result, exit.target,
+                                                  newmallocvars,
+                                                  collect_analyzer, entrymap)
+
+def find_initializing_stores(collect_analyzer, graph, entrymap):
+    # a bit of a hackish analysis: if a block contains a malloc and check that
+    # the result is not zero, then the block following the True link will
+    # usually initialize the newly allocated object
+    result = set()
     mallocnum = 0
     blockset = set(graph.iterblocks())
     while blockset:
@@ -115,7 +119,8 @@
         target = exit.target
         mallocvars = {target.inputargs[index]: True}
         mallocnum += 1
-        find_in_block(target, mallocvars)
+        propagate_no_write_barrier_needed(result, target, mallocvars,
+                                          collect_analyzer, entrymap)
     #if result:
     #    print "found %s initializing stores in %s" % (len(result), graph.name)
     return result
@@ -713,8 +718,11 @@
                                 " %s\n%s" % (func, err.getvalue()))
 
         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))
+                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))
@@ -1328,6 +1336,15 @@
                 hop.genop("direct_call", [self.write_barrier_ptr,
                                           self.c_const_gc,
                                           v_structaddr])
+                # we just did a full write barrier here, so we can use
+                # this helper to propagate this knowledge forward and
+                # avoid to repeat the write barrier.
+                if self.curr_block is not None:   # for tests
+                    propagate_no_write_barrier_needed(self.clean_sets,
+                                                      self.curr_block,
+                                                      {v_struct: True},
+                                                      self.collect_analyzer,
+                                                      self._entrymap)
         hop.rename('bare_' + opname)
 
     def transform_getfield_typeptr(self, hop):
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
@@ -1,6 +1,6 @@
 from rpython.annotator.listdef import s_list_of_strings
 from rpython.annotator.model import SomeInteger
-from rpython.flowspace.model import Constant, SpaceOperation
+from rpython.flowspace.model import Constant, SpaceOperation, mkentrymap
 from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.lltypesystem.lloperation import llop
 from rpython.memory.gc.semispace import SemiSpaceGC
@@ -244,6 +244,33 @@
          Constant('b', lltype.Void), varoftype(PTR_TYPE2)],
         varoftype(lltype.Void)))
 
+def test_remove_duplicate_write_barrier():
+    from rpython.translator.c.genc import CStandaloneBuilder
+    from rpython.flowspace.model import summary
+
+    class A(object):
+        pass
+    glob_a_1 = A()
+    glob_a_2 = A()
+
+    def f(a, cond):
+        a.x = a
+        a.z = a
+        if cond:
+            a.y = a
+    def g():
+        f(glob_a_1, 5)
+        f(glob_a_2, 0)
+    t = rtype(g, [])
+    t.config.translation.gc = "minimark"
+    cbuild = CStandaloneBuilder(t, g, t.config,
+                                gcpolicy=FrameworkGcPolicy2)
+    db = cbuild.generate_graphs_for_llinterp()
+
+    ff = graphof(t, f)
+    #ff.show()
+    assert summary(ff)['direct_call'] == 1    # only one remember_young_pointer
+
 def test_find_initializing_stores():
 
     class A(object):
@@ -259,7 +286,8 @@
     etrafo = ExceptionTransformer(t)
     graphs = etrafo.transform_completely()
     collect_analyzer = CollectAnalyzer(t)
-    init_stores = find_initializing_stores(collect_analyzer, t.graphs[0])
+    init_stores = find_initializing_stores(collect_analyzer, t.graphs[0],
+                                           mkentrymap(t.graphs[0]))
     assert len(init_stores) == 1
 
 def test_find_initializing_stores_across_blocks():
@@ -284,7 +312,8 @@
     etrafo = ExceptionTransformer(t)
     graphs = etrafo.transform_completely()
     collect_analyzer = CollectAnalyzer(t)
-    init_stores = find_initializing_stores(collect_analyzer, t.graphs[0])
+    init_stores = find_initializing_stores(collect_analyzer, t.graphs[0],
+                                           mkentrymap(t.graphs[0]))
     assert len(init_stores) == 5
 
 def test_find_clean_setarrayitems():
diff --git a/rpython/memory/gctransform/transform.py b/rpython/memory/gctransform/transform.py
--- a/rpython/memory/gctransform/transform.py
+++ b/rpython/memory/gctransform/transform.py
@@ -83,6 +83,7 @@
 
 class BaseGCTransformer(object):
     finished_helpers = False
+    curr_block = None
 
     def __init__(self, translator, inline=False):
         self.translator = translator
@@ -159,7 +160,7 @@
 
     def transform_block(self, block, is_borrowed):
         llops = LowLevelOpList()
-        #self.curr_block = block
+        self.curr_block = block
         self.livevars = [var for var in block.inputargs
                     if var_needsgc(var) and not is_borrowed(var)]
         allvars = [var for var in block.getvariables() if var_needsgc(var)]
@@ -205,6 +206,7 @@
             block.operations[:] = llops
         self.livevars = None
         self.var_last_needed_in = None
+        self.curr_block = None
 
     def transform_graph(self, graph):
         if graph in self.minimal_transform:


More information about the pypy-commit mailing list