Author: Armin Rigo <ar...@tunes.org>
Branch: shadowstack-perf-2
Changeset: r84452:12b0194d5fd3
Date: 2016-05-15 11:08 +0200
http://bitbucket.org/pypy/pypy/changeset/12b0194d5fd3/

Log:    Design and implement some algorithm

diff --git a/rpython/memory/gctransform/shadowcolor.py 
b/rpython/memory/gctransform/shadowcolor.py
--- a/rpython/memory/gctransform/shadowcolor.py
+++ b/rpython/memory/gctransform/shadowcolor.py
@@ -1,8 +1,11 @@
 from rpython.rtyper.lltypesystem import lltype, llmemory
-from rpython.flowspace.model import mkentrymap
+from rpython.flowspace.model import mkentrymap, checkgraph
 from rpython.flowspace.model import Variable, Constant, SpaceOperation
 from rpython.tool.algo.regalloc import perform_register_allocation
-from rpython.translator.unsimplify import varoftype
+from rpython.tool.algo.unionfind import UnionFind
+from rpython.translator.unsimplify import varoftype, insert_empty_block
+from rpython.translator.simplify import join_blocks
+from collections import defaultdict
 
 
 def is_trivial_rewrite(op):
@@ -171,7 +174,7 @@
         newops = []
         for op in block.operations:
             if op.opname == 'gc_push_roots':
-                newops += expand_one_push_roots(regalloc, op)
+                newops += expand_one_push_roots(regalloc, op.args)
                 any_change = True
             else:
                 newops.append(op)
@@ -182,20 +185,11 @@
 def move_pushes_earlier(graph, regalloc):
     """gc_push_roots and gc_pop_roots are pushes/pops to the shadowstack,
     immediately enclosing the operation that needs them (typically a call).
-    Here, we try to move individual pushes earlier, in fact as early as
-    possible under the following conditions: we only move it across vars
-    that are 'interesting_vars'; and we stop when we encounter the
-    operation that produces the value, or when we encounter a gc_pop_roots.
-    In the latter case, if that gc_pop_roots pops the same value out of the
-    same stack location, then success: we can remove the gc_push_root on
-    that path.
+    Here, we try to move individual pushes earlier.
 
-    If the process succeeds to remove the gc_push_root along at least
-    one path, we generate it explicitly on the other paths, and we
-    remove the original gc_push_root.  If the process doesn't succeed
-    in doing any such removal, we don't do anything.
-
-    Should run after expand_push_roots(), but before expand_pop_roots().
+    Should run after expand_push_roots(), but before expand_pop_roots(),
+    so that it sees individual 'gc_save_root' operations but bulk
+    'gc_pop_roots' operations.
     """
     # Concrete example (assembler tested on x86-64 gcc 5.3 and clang 3.7):
     #
@@ -213,49 +207,129 @@
     # load are in the loop,      moves the load after the loop
     # even in the assembler      (the commented-out '*foo=b' is removed
     #                            here, but gcc/clang would also remove it)
-    
-    
-    xxxxxxxxxxxx
+
+    # Draft of the algorithm: see shadowcolor.txt
+
     if not regalloc:
         return
 
-    process = []
-    for block in graph.iterblocks():    # XXX better order?
-        for op in block.operations:
-            if op.opname == 'gc_save_root':
-                if isinstance(op.args[1], Variable):
-                    process.append((block, op))
+    Plist = []
+
+    for i in range(regalloc.numcolors):
+        U = UnionFind()
+
+        S = set()
+        for block in graph.iterblocks():
+            for op in reversed(block.operations):
+                # XXX handle renames
+                if op.opname == 'gc_pop_roots':
+                    break
             else:
-                assert op.opname != 'gc_restore_root'
+                continue   # no gc_pop_roots in this block
+            for v in op.args:
+                if regalloc.getcolor(v) == i:
+                    break
+            else:
+                continue   # no variable goes into index i
+            lst = list(find_successors(graph, [(block, v)]))
+            U.union_list(lst)
+            S.update(lst)
 
-    for initial_block, op_save in process:
-        new_block_locations = []
-        new_link_locations = []
-        num_removed = 0
-        pending = [(initial_block, op_save)]
-        while pending:
-            block, v = pending.pop()
-            
-            
-            if v in block.inputargs:
-                xxxx
+        G = defaultdict(set)
+        for block in graph.iterblocks():
+            for op in block.operations:
+                # XXX handle renames
+                if op.opname == 'gc_save_root' and op.args[0].value == i:
+                    break
             else:
-                for op in block.operations:
-                    if op.result is v:
-                        if is_trivial_rewrite(op):
-                            pending.append((block, op.args[0]))
-                        else:
-                            new_block_locations = [(block, op)]
-                        break
-                    elif op.opname == 'gc_pop_roots':
-                        yyyyyy
-                        break
-                else:
-                    raise AssertionError("%r: no origin for %r in block %r"
-                                         % (graph, v, block))
+                continue   # no matching gc_save_root in this block
+            lst = list(find_predecessors(graph, [(block, op.args[1])]))
+            U.union_list(lst)
+            for v1 in lst:
+                G[v1].add((block, op))
 
+        M = S.intersection(G)
 
-def expand_pop_roots(graph):
+        parts_target = {}
+        for v in M:
+            vp = U.find_rep(v)
+            if vp not in parts_target:
+                new_part = (i, set(), set())
+                # (index,
+                #  subset P of variables,
+                #  set of (block, gc_save_root))
+                Plist.append(new_part)
+                parts_target[vp] = new_part
+            part = parts_target[vp]
+            part[1].add(v)
+            part[2].update(G[v])
+
+    #P.sort(...heuristic?)
+
+    entrymap = mkentrymap(graph)
+    inputvars = {}    # {inputvar: (its block, its index in inputargs)}
+    for block in graph.iterblocks():
+        for i, v in enumerate(block.inputargs):
+            inputvars[v] = (block, i)
+
+    variables_along_changes = set()
+
+    for i, P, gcsaveroots in Plist:
+        if variables_along_changes.intersection(P):
+            continue
+        if any(op not in block.operations for block, op in gcsaveroots):
+            continue
+
+        success = False
+        mark = []
+
+        for v in P:
+            try:
+                block, varindex = inputvars[v]
+            except KeyError:
+                continue
+            for link in entrymap[block]:
+                w = link.args[varindex]
+                maybe_found = True   # unless proven false
+                try:
+                    if regalloc.getcolor(w) != i:
+                        maybe_found = False
+                except KeyError:
+                    maybe_found = False
+                if maybe_found:
+                    for op in reversed(link.prevblock.operations):
+                        # XXX handle renames
+                        if op.opname == 'gc_pop_roots':
+                            if w in op.args:
+                                success = True
+                            else:
+                                maybe_found = False
+                            break
+                    else:
+                        maybe_found = False
+                if not maybe_found:
+                    if w not in P:
+                        mark.append((link, varindex))
+
+        if success:
+            for block, op in gcsaveroots:
+                newops = list(block.operations)
+                newops.remove(op)
+                block.operations = newops
+
+            for link, varindex in mark:
+                newblock = insert_empty_block(link)
+                v = newblock.inputargs[varindex]
+                newblock.operations.append(_gc_save_root(i, v))
+
+            variables_along_changes.update(P)
+
+    if variables_along_changes:     # if there was any change
+        checkgraph(graph)
+        join_blocks(graph)
+
+
+def expand_pop_roots(graph, regalloc):
     """gc_pop_roots => series of gc_restore_root; this is done after
     move_pushes_earlier() because that one doesn't work correctly if
     a completely-empty gc_pop_roots is removed.
@@ -265,7 +339,7 @@
         newops = []
         for op in block.operations:
             if op.opname == 'gc_pop_roots':
-                newops += expand_one_pop_roots(regalloc, op)
+                newops += expand_one_pop_roots(regalloc, op.args)
                 any_change = True
             else:
                 newops.append(op)
diff --git a/rpython/memory/gctransform/shadowcolor.txt 
b/rpython/memory/gctransform/shadowcolor.txt
new file mode 100644
--- /dev/null
+++ b/rpython/memory/gctransform/shadowcolor.txt
@@ -0,0 +1,46 @@
+
+
+for every frame index i:
+
+  S_i = { variable: non-empty-set-of-sources }
+
+    source = a variable popped by gc_pop_roots from frame index 'i',
+             only looking at the last gc_pop_roots in each block
+    keys = variables that appear in inputargs, where a value from a
+           'source' can come from
+
+  G_i = { variable: non-empty-set-of-targets }
+
+    target = a variable pushed by gc_push_roots into frame index 'i',
+             only looking at the first gc_push_roots in each block
+    keys = variables that appear in inputargs, where a value can
+           end up in a 'target'
+
+  M_i = S_i intersection G_i
+
+  "variable in M_i"  <==>  at the start of this block, this variable's
+  value is already saved in the frame index i (provided "success" below)
+
+  split M_i into a partition of independent parts; add (i, P), (i, P'), ...
+  to the global list
+
+
+for every (i, P), ideally in some suitable order:
+
+   for every variable in P, for every link entering this block:
+
+      if prevblock's corresponding variable is from the last gc_pop_roots
+      of that block, at index i:
+
+        *success = True*
+
+      elif prevblock's corresponding variable is not in P:
+
+        mark the link
+
+   if success:
+
+      insert a new gc_save_root() along all links marked above;
+      remove the original gc_save_root
+
+      for any P' after P that has any variables in common, kill that P'
diff --git a/rpython/memory/gctransform/test/test_shadowcolor.py 
b/rpython/memory/gctransform/test/test_shadowcolor.py
--- a/rpython/memory/gctransform/test/test_shadowcolor.py
+++ b/rpython/memory/gctransform/test/test_shadowcolor.py
@@ -3,6 +3,7 @@
 from rpython.rtyper.test.test_llinterp import gengraph
 from rpython.conftest import option
 from rpython.memory.gctransform.shadowcolor import *
+from rpython.flowspace import model as graphmodel
 from hypothesis import given, strategies
 
 
@@ -309,3 +310,28 @@
     assert regalloc.check(expand_one_pop_roots(regalloc, [])) == []
 
     assert list(expand_one_pop_roots(None, [])) == []
+
+def test_move_pushes_earlier():
+    def g(a):
+        return a - 1
+    def f(a, b):
+        while a > 10:
+            llop.gc_push_roots(lltype.Void, b)
+            a = g(a)
+            llop.gc_pop_roots(lltype.Void, b)
+        return b
+
+    graph = make_graph(f, [int, llmemory.GCREF])
+    regalloc = allocate_registers(graph)
+    expand_push_roots(graph, regalloc)
+    move_pushes_earlier(graph, regalloc)
+    expand_pop_roots(graph, regalloc)
+    assert graphmodel.summary(graph) == {
+        'gc_save_root': 1,
+        'gc_restore_root': 1,
+        'int_gt': 1,
+        'direct_call': 1,
+        }
+    assert len(graph.startblock.operations) == 1
+    assert graph.startblock.operations[0].opname == 'gc_save_root'
+    assert graph.startblock.operations[0].args[0].value == 0
diff --git a/rpython/tool/algo/unionfind.py b/rpython/tool/algo/unionfind.py
--- a/rpython/tool/algo/unionfind.py
+++ b/rpython/tool/algo/unionfind.py
@@ -89,3 +89,11 @@
         self.root_info[rep1] = info1
 
         return True, rep1, info1
+
+    def union_list(self, objlist):
+        if len(objlist) == 0:
+            return
+        obj0 = objlist[0]
+        self.find(obj0)
+        for obj1 in objlist[1:]:
+            self.union(obj0, obj1)
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to