Author: Armin Rigo <[email protected]>
Branch: shadowstack-perf-2
Changeset: r84384:56e5c4403abf
Date: 2016-05-11 20:22 +0200
http://bitbucket.org/pypy/pypy/changeset/56e5c4403abf/

Log:    in-progress, hypothesis testing of the bitmask encoding

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,5 +1,7 @@
-from rpython.flowspace.model import mkentrymap, Variable
+from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.flowspace.model import mkentrymap, Variable, Constant
 from rpython.tool.algo.regalloc import perform_register_allocation
+from rpython.translator.unsimplify import varoftype
 
 
 def is_trivial_rewrite(op):
@@ -77,6 +79,8 @@
                 for v in op.args:
                     assert v in interesting_vars   # must be pushed just above
                     pending_succ.append((block, v))
+    if not interesting_vars:
+        return None
 
     # If there is a path from a gc_pop_roots(v) to a subsequent
     # gc_push_roots(w) where w contains the same value as v along that
@@ -96,36 +100,115 @@
 
 def allocate_registers(graph):
     interesting_vars = find_interesting_variables(graph)
+    if not interesting_vars:
+        return None
     regalloc = perform_register_allocation(graph, 
interesting_vars.__contains__)
+    regalloc.find_num_colors()
     return regalloc
 
 
-def move_pushes_earlier(graph):
+def _gc_save_root(index, var):
+    c_index = Constant(index, lltype.Signed)
+    return SpaceOperation('gc_save_root', [c_index, var],
+                          varoftype(lltype.Void))
+
+c_NULL = Constant(lltype.nullptr(llmemory.GCREF.TO), llmemory.GCREF)
+
+def make_bitmask(filled):
+    n = filled.count(False)
+    if n == 0:
+        return (None, None)
+    if n == 1:
+        return (filled.index(False), c_NULL)
+    bitmask = 0
+    last_index = 0
+    for i in range(len(filled)):
+        if not filled[i]:
+            bitmask <<= (i - last_index)
+            last_index = i
+            bitmask |= 1
+    return (last_index, Constant(bitmask, lltype.Signed))
+
+
+def expand_push_roots(graph, regalloc):
+    """Expand gc_push_roots into a series of gc_save_root, including
+    writing a bitmask tag to mark some entries as not-in-use
+    """
+    for block in graph.iterblocks():
+        any_change = False
+        newops = []
+        for op in block.operations:
+            if op.opname == 'gc_push_roots':
+                if regalloc is None:
+                    assert len(op.args) == 0
+                else:
+                    filled = [False] * regalloc.numcolors
+                    for v in op.args:
+                        index = regalloc.getcolor(v)
+                        assert not filled[index]
+                        filled[index] = True
+                        newops.append(_gc_save_root(index, v))
+                    bitmask_index, bitmask_v = make_bitmask(filled)
+                    if bitmask_index is not None:
+                        newops.append(_gc_save_root(bitmask_index, bitmask_v))
+                any_change = True
+            else:
+                newops.append(op)
+        if any_change:
+            block.operations = newops
+
+
+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
-    that pops off the same stack location.  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.
+    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.
 
     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.
+    """
+    # Concrete example (assembler tested on x86-64 gcc 5.3 and clang 3.7):
+    #
+    # ----original----           ----move_pushes_earlier----
+    #
+    # while (a > 10) {           *foo = b;
+    #     *foo = b;              while (a > 10) {
+    #     a = g(a);                  a = g(a);
+    #     b = *foo;                  b = *foo;
+    #                                // *foo = b;
+    # }                          }
+    # return b;                  return b;
+    #
+    # => the store and the       => the store is before, and gcc/clang
+    # load are in the loop,      moves the load after the loop
+    # even in the assembler      (the commented-out '*foo=b' is removed
+    #                            by this function, but gcc/clang would
+    #                            also remove it)
 
-    Note that it would be possible to do exactly the same in the
-    opposite direction by exchanging the roles of "push/earlier" and
-    "pop/later".  I think doing both is pointless---one direction is
-    enough.  The direction we chose here keeps gc_pop_roots unmodified.
-    The C compiler should be better at discarding them if unused.
-    """
-    
     x.x.x.x
 
 
+def expand_push_pop_roots(graph):
+    xxxxxxxxx
+    for block in graph.iterblocks():
+        for op in block.operations:
+            if op.opname == 'gc_push_roots':
+                for v in op.args:
+                    interesting_vars.add(v)
+                    pending_pred.append((block, v))
+            elif op.opname == 'gc_pop_roots':
+                for v in op.args:
+                    assert v in interesting_vars   # must be pushed just above
+                    pending_succ.append((block, v))
+
+
 def postprocess_graph(gct, graph):
     """Collect information about the gc_push_roots and gc_pop_roots
     added in this complete graph, and replace them with real operations.
diff --git a/rpython/memory/gctransform/shadowstack.py 
b/rpython/memory/gctransform/shadowstack.py
--- a/rpython/memory/gctransform/shadowstack.py
+++ b/rpython/memory/gctransform/shadowstack.py
@@ -29,11 +29,14 @@
     def push_roots(self, hop, keep_current_args=False):
         livevars = self.get_livevars_for_roots(hop, keep_current_args)
         self.num_pushs += len(livevars)
-        hop.genop("gc_push_roots", livevars)   # even if len(livevars) == 0
+        if livevars:
+            hop.genop("gc_push_roots", livevars)
         return livevars
 
     def pop_roots(self, hop, livevars):
-        hop.genop("gc_pop_roots", list(livevars)) # even if len(livevars) == 0
+        hop.genop("gc_pop_roots", livevars)
+        # NB. we emit it even if len(livevars) == 0; this is needed for
+        # shadowcolor.move_pushes_earlier()
 
 
 class ShadowStackRootWalker(BaseRootWalker):
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 hypothesis import given, strategies
 
 
 def make_graph(f, argtypes):
@@ -242,3 +243,25 @@
     graph = make_graph(f, [llmemory.GCREF, int, llmemory.GCREF])
     regalloc = allocate_registers(graph)
     assert summary_regalloc(regalloc) == [('a', 1)] * 2 + [('c', 0)] * 2
+
+@given(strategies.lists(strategies.booleans()))
+def test_make_bitmask(boollist):
+    index, c = make_bitmask(boollist)
+    if index is None:
+        assert c is None
+    else:
+        assert 0 <= index < len(boollist)
+        assert boollist[index] == False
+        if c == c_NULL:
+            bitmask = 1
+        else:
+            assert c.concretetype == lltype.Signed
+            bitmask = c.value
+        while bitmask:
+            if bitmask & 1:
+                assert index >= 0
+                assert boollist[index] == False
+                boollist[index] = True
+            bitmask >>= 1
+            index -= 1
+    assert boollist == [True] * len(boollist)
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
@@ -513,8 +513,12 @@
     'gc_rawrefcount_from_obj':          LLOp(sideeffects=False),
     'gc_rawrefcount_to_obj':            LLOp(sideeffects=False),
 
-    'gc_push_roots'       : LLOp(),
-    'gc_pop_roots'        : LLOp(),
+    'gc_push_roots'        : LLOp(),  # temporary: list of roots to save
+    'gc_pop_roots'         : LLOp(),  # temporary: list of roots to restore
+    'gc_enter_roots_frame' : LLOp(),  # reserve N entries, save local frame pos
+    'gc_leave_roots_frame' : LLOp(),  # restore shadowstack ptr from saved pos
+    'gc_save_root'         : LLOp(),  # save value Y in shadowstack pos X
+    'gc_restore_root'      : LLOp(),  # restore value Y from shadowstack pos X
 
     # ------- JIT & GC interaction, only for some GCs ----------
 
diff --git a/rpython/tool/algo/regalloc.py b/rpython/tool/algo/regalloc.py
--- a/rpython/tool/algo/regalloc.py
+++ b/rpython/tool/algo/regalloc.py
@@ -117,6 +117,13 @@
                 for v in block.getvariables():
                     print '\t', v, '\t', self.getcolor(v)
 
+    def find_num_colors(self):
+        if self._coloring:
+            numcolors = max(self._coloring.values()) + 1
+        else:
+            numcolors = 0
+        self.numcolors = numcolors
+
     def getcolor(self, v):
         return self._coloring[self._unionfind.find_rep(v)]
 
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to