[pypy-svn] r79842 - in pypy/branch/inline-shadowstack/pypy: config jit/codewriter jit/codewriter/test tool/algo tool/algo/test translator/c translator/c/src

arigo at codespeak.net arigo at codespeak.net
Mon Dec 6 11:38:38 CET 2010


Author: arigo
Date: Mon Dec  6 11:38:36 2010
New Revision: 79842

Added:
   pypy/branch/inline-shadowstack/pypy/tool/algo/regalloc.py
      - copied, changed from r79813, pypy/branch/inline-shadowstack/pypy/jit/codewriter/regalloc.py
   pypy/branch/inline-shadowstack/pypy/tool/algo/test/test_regalloc.py
Removed:
   pypy/branch/inline-shadowstack/pypy/jit/codewriter/regalloc.py
Modified:
   pypy/branch/inline-shadowstack/pypy/config/translationoption.py
   pypy/branch/inline-shadowstack/pypy/jit/codewriter/codewriter.py
   pypy/branch/inline-shadowstack/pypy/jit/codewriter/test/test_regalloc.py
   pypy/branch/inline-shadowstack/pypy/translator/c/funcgen.py
   pypy/branch/inline-shadowstack/pypy/translator/c/gc.py
   pypy/branch/inline-shadowstack/pypy/translator/c/genc.py
   pypy/branch/inline-shadowstack/pypy/translator/c/src/mem.h
Log:
First implementation.  The results are not great on targetgcbench.


Modified: pypy/branch/inline-shadowstack/pypy/config/translationoption.py
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/config/translationoption.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/config/translationoption.py	Mon Dec  6 11:38:36 2010
@@ -86,14 +86,16 @@
                default=IS_64_BITS, cmdline="--gcremovetypeptr"),
     ChoiceOption("gcrootfinder",
                  "Strategy for finding GC Roots (framework GCs only)",
-                 ["n/a", "shadowstack", "asmgcc"],
+                 ["n/a", "shadowstack", "asmgcc", "inlinestack"],
                  "shadowstack",
                  cmdline="--gcrootfinder",
                  requires={
-                     "shadowstack": [("translation.gctransformer", "framework")],
-                     "asmgcc": [("translation.gctransformer", "framework"),
-                                ("translation.backend", "c")],
-                    }),
+                   "shadowstack": [("translation.gctransformer", "framework")],
+                   "asmgcc": [("translation.gctransformer", "framework"),
+                              ("translation.backend", "c")],
+                   "inlinestack": [("translation.gctransformer", "framework"),
+                                   ("translation.backend", "c")],
+                   }),
 
     # other noticeable options
     BoolOption("thread", "enable use of threading primitives",

Modified: pypy/branch/inline-shadowstack/pypy/jit/codewriter/codewriter.py
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/jit/codewriter/codewriter.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/jit/codewriter/codewriter.py	Mon Dec  6 11:38:36 2010
@@ -1,5 +1,6 @@
 from pypy.jit.codewriter import support, heaptracker
-from pypy.jit.codewriter.regalloc import perform_register_allocation
+from pypy.tool.algo.regalloc import perform_register_allocation
+from pypy.jit.metainterp.history import getkind
 from pypy.jit.codewriter.flatten import flatten_graph, KINDS
 from pypy.jit.codewriter.assembler import Assembler, JitCode
 from pypy.jit.codewriter.jtransform import transform_graph
@@ -43,7 +44,8 @@
         # step 2: perform register allocation on it
         regallocs = {}
         for kind in KINDS:
-            regallocs[kind] = perform_register_allocation(graph, kind)
+            kind_filter = lambda v: getkind(v.concretetype) == kind
+            regallocs[kind] = perform_register_allocation(graph, kind_filter)
         #
         # step 3: flatten the graph to produce human-readable "assembler",
         # which means mostly producing a linear list of operations and

Modified: pypy/branch/inline-shadowstack/pypy/jit/codewriter/test/test_regalloc.py
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/jit/codewriter/test/test_regalloc.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/jit/codewriter/test/test_regalloc.py	Mon Dec  6 11:38:36 2010
@@ -1,9 +1,9 @@
 import py, sys
 from pypy.jit.codewriter import support
-from pypy.jit.codewriter.regalloc import perform_register_allocation
+from pypy.tool.algo.regalloc import perform_register_allocation
 from pypy.jit.codewriter.flatten import flatten_graph, ListOfKind
 from pypy.jit.codewriter.format import assert_format
-from pypy.jit.metainterp.history import AbstractDescr
+from pypy.jit.metainterp.history import AbstractDescr, getkind
 from pypy.objspace.flow.model import Variable, Constant, SpaceOperation
 from pypy.objspace.flow.model import FunctionGraph, Block, Link
 from pypy.objspace.flow.model import c_last_exception
@@ -18,6 +18,10 @@
         self.rtyper = support.annotate(func, values, type_system=type_system)
         return self.rtyper.annotator.translator.graphs
 
+    def perform_register_allocation(self, graph, kind):
+        kind_filter = lambda v: getkind(v.concretetype) == kind
+        return perform_register_allocation(graph, kind_filter)
+
     def check_assembler(self, graph, expected, transform=False,
                         callcontrol=None):
         # 'transform' can be False only for simple graphs.  More complex
@@ -26,8 +30,8 @@
         if transform:
             from pypy.jit.codewriter.jtransform import transform_graph
             transform_graph(graph, callcontrol=callcontrol)
-        regalloc = perform_register_allocation(graph, 'int')
-        regalloc2 = perform_register_allocation(graph, 'ref')
+        regalloc = self.perform_register_allocation(graph, 'int')
+        regalloc2 = self.perform_register_allocation(graph, 'ref')
         ssarepr = flatten_graph(graph, {'int': regalloc,
                                         'ref': regalloc2})
         assert_format(ssarepr, expected)
@@ -36,7 +40,7 @@
         def f(a, b):
             return a + b
         graph = self.make_graphs(f, [5, 6])[0]
-        regalloc = perform_register_allocation(graph, 'int')
+        regalloc = self.perform_register_allocation(graph, 'int')
         va, vb = graph.startblock.inputargs
         vc = graph.startblock.operations[0].result
         assert regalloc.getcolor(va) == 0
@@ -50,7 +54,7 @@
                 a -= 1
             return b
         graph = self.make_graphs(f, [5, 6])[0]
-        regalloc = perform_register_allocation(graph, 'float')
+        regalloc = self.perform_register_allocation(graph, 'float')
         # assert did not crash
 
     def test_regalloc_loop(self):
@@ -285,18 +289,18 @@
         self.check_assembler(graph, """
             residual_call_r_r $<* fn bar>, <Descr>, R[%r0] -> %r1
             -live-
-            residual_call_ir_r $<* fn g>, <Descr>, I[%i0], R[] -> %r2
+            residual_call_ir_r $<* fn g>, <Descr>, I[%i0], R[] -> %r1
             -live-
             catch_exception L1
-            ref_return %r2
+            ref_return %r1
             ---
             L1:
             goto_if_exception_mismatch $<* struct object_vtable>, L2
-            ref_copy %r0 -> %r2
+            ref_copy %r0 -> %r1
             last_exc_value -> %r0
             residual_call_r_r $<* fn foo>, <Descr>, R[%r0] -> %r0
             -live-
-            ref_return %r2
+            ref_return %r1
             ---
             L2:
             reraise

Copied: pypy/branch/inline-shadowstack/pypy/tool/algo/regalloc.py (from r79813, pypy/branch/inline-shadowstack/pypy/jit/codewriter/regalloc.py)
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/jit/codewriter/regalloc.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/tool/algo/regalloc.py	Mon Dec  6 11:38:36 2010
@@ -2,13 +2,12 @@
 from pypy.objspace.flow.model import Variable
 from pypy.tool.algo.color import DependencyGraph
 from pypy.tool.algo.unionfind import UnionFind
-from pypy.jit.metainterp.history import getkind
-from pypy.jit.codewriter.flatten import ListOfKind
 
-def perform_register_allocation(graph, kind):
-    """Perform register allocation for the Variables of the given 'kind'
-    in the 'graph'."""
-    regalloc = RegAllocator(graph, kind)
+def perform_register_allocation(graph, kind_filter, identity_op_filter=None):
+    """Perform register allocation for the Variables of the given kind
+    in the graph.  
+    """
+    regalloc = RegAllocator(graph, kind_filter, identity_op_filter)
     regalloc.make_dependencies()
     regalloc.coalesce_variables()
     regalloc.find_node_coloring()
@@ -18,12 +17,18 @@
 class RegAllocator(object):
     DEBUG_REGALLOC = False
 
-    def __init__(self, graph, kind):
+    def __init__(self, graph, kind_filter, identity_op_filter=None):
         self.graph = graph
-        self.kind = kind
+        self.kind_filter = kind_filter
+        if identity_op_filter is not None:
+            self.identity_op_filter = identity_op_filter
+
+    def identity_op_filter(self, op):
+        return False     # default implementation
 
     def make_dependencies(self):
         dg = DependencyGraph()
+        uf = UnionFind()
         for block in self.graph.iterblocks():
             # Compute die_at = {Variable: index_of_operation_with_last_usage}
             die_at = dict.fromkeys(block.inputargs, 0)
@@ -31,13 +36,13 @@
                 for v in op.args:
                     if isinstance(v, Variable):
                         die_at[v] = i
-                    elif isinstance(v, ListOfKind):
+                    elif is_iterable(v):
                         for v1 in v:
                             if isinstance(v1, Variable):
                                 die_at[v1] = i
                 if op.result is not None:
-                    die_at[op.result] = i
-            if isinstance(block.exitswitch, tuple):
+                    die_at[op.result] = i + 1
+            if is_iterable(block.exitswitch):
                 for x in block.exitswitch:
                     die_at.pop(x, None)
             else:
@@ -48,10 +53,10 @@
             die_at = [(value, key) for (key, value) in die_at.items()]
             die_at.sort()
             die_at.append((sys.maxint,))
-            # Done.  XXX the code above this line runs 3 times
-            # (for kind in KINDS) to produce the same result...
-            livevars = [v for v in block.inputargs
-                          if getkind(v.concretetype) == self.kind]
+            # Done.  XXX if we need to perform register allocation on
+            # the same graph with various 'kinds', the code above this
+            # line runs several times to produce the same result...
+            livevars = [v for v in block.inputargs if self.kind_filter(v)]
             # Add the variables of this block to the dependency graph
             for i, v in enumerate(livevars):
                 dg.add_node(v)
@@ -66,17 +71,21 @@
                     except KeyError:
                         pass
                     die_index += 1
-                if (op.result is not None and
-                    getkind(op.result.concretetype) == self.kind):
-                    dg.add_node(op.result)
-                    for v in livevars:
-                        if getkind(v.concretetype) == self.kind:
-                            dg.add_edge(v, op.result)
+                if op.result is not None and self.kind_filter(op.result):
+                    if self.identity_op_filter(op):
+                        rep1 = uf.find_rep(op.args[0])
+                        _, rep2, _ = uf.union(rep1, op.result)
+                        assert rep2 is rep1
+                    else:
+                        dg.add_node(op.result)
+                        for v in livevars:
+                            assert self.kind_filter(v)
+                            dg.add_edge(uf.find_rep(v), op.result)
                     livevars.add(op.result)
         self._depgraph = dg
+        self._unionfind = uf
 
     def coalesce_variables(self):
-        self._unionfind = UnionFind()
         pendingblocks = list(self.graph.iterblocks())
         while pendingblocks:
             block = pendingblocks.pop()
@@ -95,7 +104,7 @@
                     self._try_coalesce(v, link.target.inputargs[i])
 
     def _try_coalesce(self, v, w):
-        if isinstance(v, Variable) and getkind(v.concretetype) == self.kind:
+        if isinstance(v, Variable) and self.kind_filter(v):
             dg = self._depgraph
             uf = self._unionfind
             v0 = uf.find_rep(v)
@@ -126,3 +135,11 @@
                 self._coloring[key] = col2
             elif value == col2:
                 self._coloring[key] = col1
+
+
+def is_iterable(x):
+    try:
+        iter(x)
+        return True
+    except (TypeError, AttributeError):
+        return False

Added: pypy/branch/inline-shadowstack/pypy/tool/algo/test/test_regalloc.py
==============================================================================
--- (empty file)
+++ pypy/branch/inline-shadowstack/pypy/tool/algo/test/test_regalloc.py	Mon Dec  6 11:38:36 2010
@@ -0,0 +1,82 @@
+
+# XXX not enough tests, but see also jit/codewriter/test/test_regalloc.py
+
+import py
+from pypy.tool.algo.regalloc import perform_register_allocation
+from pypy.rpython.test.test_llinterp import gengraph
+from pypy.rpython.lltypesystem import lltype
+from pypy.rlib.jit import hint
+
+
+def test_simple():
+    def f(a, b, c):
+        return (b * c) + a
+    def kind_float(v):
+        return v.concretetype == lltype.Float
+
+    t, rtyper, graph = gengraph(f, [int, int, float])
+    assert [op.opname for op in graph.startblock.operations] == [
+        "cast_int_to_float",    # (1) = b
+        "float_mul",            # (0) = (1) * (0)
+        "cast_int_to_float",    # (1) = a
+        "float_add"]            # (0) = (1) + (0)
+
+    regalloc = perform_register_allocation(graph, kind_float)
+
+    py.test.raises(KeyError, regalloc.getcolor, graph.getargs()[0])
+    py.test.raises(KeyError, regalloc.getcolor, graph.getargs()[1])
+
+    ops = graph.startblock.operations
+    assert regalloc.getcolor(graph.getargs()[2]) == 0
+    assert regalloc.getcolor(ops[0].result) == 1
+    assert regalloc.getcolor(ops[1].result) == 0
+    assert regalloc.getcolor(ops[2].result) == 1
+    assert regalloc.getcolor(ops[3].result) == 0
+    assert regalloc.getcolor(graph.getreturnvar()) == 0
+
+def test_unused_result():
+    def f(x):
+        hint(x, blah=True)
+        hint(x, blah=True)
+        hint(x, blah=True)
+        return x
+    def kind_float(v):
+        return v.concretetype == lltype.Float
+
+    t, rtyper, graph = gengraph(f, [lltype.Float])
+    assert [op.opname for op in graph.startblock.operations] == [
+        "hint",   # (1) = hint(0)
+        "hint",   # (1) = hint(0)
+        "hint"]   # (1) = hint(0)
+
+    regalloc = perform_register_allocation(graph, kind_float)
+    ops = graph.startblock.operations
+    assert regalloc.getcolor(graph.getargs()[0]) == 0
+    assert regalloc.getcolor(ops[0].result) == 1
+    assert regalloc.getcolor(ops[1].result) == 1
+    assert regalloc.getcolor(ops[2].result) == 1
+    assert regalloc.getcolor(graph.getreturnvar()) == 0
+
+def test_identity_op():
+    def f(x):
+        y = hint(x, blah=True)
+        z = hint(y, blah=True)
+        t = hint(x, blah=True)
+        return 0
+    def kind_float(v):
+        return v.concretetype == lltype.Float
+    def identity_op(op):
+        return op.opname == 'hint'
+
+    t, rtyper, graph = gengraph(f, [lltype.Float])
+    assert [op.opname for op in graph.startblock.operations] == [
+        "hint",   # (0) = hint(0)
+        "hint",   # (0) = hint(0)
+        "hint"]   # (0) = hint(0)
+
+    regalloc = perform_register_allocation(graph, kind_float, identity_op)
+    ops = graph.startblock.operations
+    assert regalloc.getcolor(graph.getargs()[0]) == 0
+    assert regalloc.getcolor(ops[0].result) == 0
+    assert regalloc.getcolor(ops[1].result) == 0
+    assert regalloc.getcolor(ops[2].result) == 0

Modified: pypy/branch/inline-shadowstack/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/translator/c/funcgen.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/translator/c/funcgen.py	Mon Dec  6 11:38:36 2010
@@ -176,6 +176,9 @@
             if self.lltypemap(v) is Void and special_case_void:
                 return '/* nothing */'
             else:
+                s = self.gcpolicy.special_ref_name_for_local(v)
+                if s:
+                    return s
                 return LOCALVAR % v.name
         elif isinstance(v, Constant):
             value = llvalue_from_constant(v)
@@ -190,6 +193,7 @@
 
     def cfunction_declarations(self):
         # declare the local variables, excluding the function arguments
+        self.gcpolicy.prepare_declarations_in_function(self)
         seen = {}
         for a in self.graph.getargs():
             seen[a.name] = True
@@ -199,6 +203,8 @@
             name = v.name
             if name not in seen:
                 seen[name] = True
+                if self.gcpolicy.special_ref_name_for_local(v):
+                    continue
                 result = cdecl(self.lltypename(v), LOCALVAR % name) + ';'
                 if self.lltypemap(v) is Void:
                     continue  #result = '/*%s*/' % result
@@ -209,6 +215,8 @@
     # ____________________________________________________________
 
     def cfunction_body(self):
+        for line in self.gcpolicy.extra_declarations_in_function(self):
+            yield line
         graph = self.graph
         yield 'goto block0;'    # to avoid a warning "this label is not used"
 
@@ -232,6 +240,8 @@
                 retval = self.expr(block.inputargs[0])
                 if self.exception_policy != "exc_helper":
                     yield 'RPY_DEBUG_RETURN();'
+                for line in self.gcpolicy.extra_code_at_return(self):
+                    yield line
                 yield 'return %s;' % retval
                 continue
             elif block.exitswitch is None:
@@ -306,7 +316,7 @@
             if a2type is Void:
                 continue
             src = self.expr(a1)
-            dest = LOCALVAR % a2.name
+            dest = self.expr(a2)
             assignments.append((a2typename, dest, src))
         for line in gen_assignments(assignments):
             yield line

Modified: pypy/branch/inline-shadowstack/pypy/translator/c/gc.py
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/translator/c/gc.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/translator/c/gc.py	Mon Dec  6 11:38:36 2010
@@ -1,12 +1,12 @@
 import sys
-from pypy.objspace.flow.model import Constant
+from pypy.objspace.flow.model import Constant, Variable
 from pypy.translator.c.support import cdecl
 from pypy.translator.c.node import ContainerNode
 from pypy.rpython.lltypesystem.lltype import \
      typeOf, Ptr, ContainerType, RttiStruct, \
      RuntimeTypeInfo, getRuntimeTypeInfo, top_container
 from pypy.rpython.memory.gctransform import \
-     refcounting, boehm, framework, asmgcroot
+     refcounting, boehm, framework, asmgcroot, inlinestack
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.translator.tool.cbuild import ExternalCompilationInfo
 
@@ -70,6 +70,15 @@
     def rtti_type(self):
         return ''
 
+    def prepare_declarations_in_function(self, funcgen):
+        pass
+    def special_ref_name_for_local(self, v):
+        return False
+    def extra_declarations_in_function(self, funcgen):
+        return []
+    def extra_code_at_return(self, funcgen):
+        return []
+
     def OP_GC_PUSH_ALIVE_PYOBJ(self, funcgen, op):
         expr = funcgen.expr(op.args[0])
         if expr == 'NULL':
@@ -384,6 +393,65 @@
     def OP_GC_STACK_BOTTOM(self, funcgen, op):
         return 'pypy_asm_stack_bottom();'
 
+class InlineStackFrameworkGcPolicy(FrameworkGcPolicy):
+    transformerclass = inlinestack.InlineStackFrameworkGCTransformer
+
+    def prepare_declarations_in_function(self, funcgen):
+        self.gcvar2index = {}    # xxx should be stored on 'funcgen', ideally
+        self.all_gcvars = []
+        for block in funcgen.graph.iterblocks():
+            for op in block.operations:
+                if op.opname == 'gc_push_roots':
+                    for v in op.args:
+                        if not isinstance(v, Variable):
+                            continue
+                        assert funcgen.lltypemap(v) is not lltype.Void
+                        vname = v.name
+                        if vname not in self.gcvar2index:
+                            self.gcvar2index[vname] = len(self.all_gcvars)
+                            self.all_gcvars.append((funcgen.lltypename(v),
+                                                    vname))
+
+    def special_ref_name_for_local(self, v):
+        if v.name in self.gcvar2index:
+            return 'ref.%s' % v.name
+        return None
+
+    def extra_declarations_in_function(self, funcgen):
+        if self.all_gcvars:
+            yield 'struct {'
+            yield '\tstruct pypy_stackref_s hdr;'
+            assert len(self.all_gcvars) < 32      # XXX fix this limitation
+            for vtype, vname in self.all_gcvars:
+                yield '\t%s;' % cdecl(vtype, vname)
+            yield '} ref;'
+            #
+            yield 'ref.hdr.prev = pypy_stackref;'
+            yield 'pypy_stackref = &ref.hdr;'
+            for argname, v in zip(funcgen.argnames(), funcgen.graph.getargs()):
+                s = self.special_ref_name_for_local(v)
+                if s:
+                    yield '%s = %s;' % (s, argname)
+
+    def extra_code_at_return(self, funcgen):
+        if self.all_gcvars:
+            yield 'pypy_stackref = ref.hdr.prev;'
+
+    def OP_GC_PUSH_ROOTS(self, funcgen, op):
+        bitfield = 0
+        vars = []
+        for v in op.args:
+            if isinstance(v, Variable):
+                bitfield |= 1 << self.gcvar2index[v.name]
+                vars.append(v.name)
+        if not self.all_gcvars:
+            assert not vars
+            return ''
+        result = 'ref.hdr.bitfield = %d;' % bitfield
+        if vars:
+            result += ' /* %s */' % (' '.join(vars),)
+        return result
+
 
 name_to_gcpolicy = {
     'boehm': BoehmGcPolicy,
@@ -391,6 +459,5 @@
     'none': NoneGcPolicy,
     'framework': FrameworkGcPolicy,
     'framework+asmgcroot': AsmGcRootFrameworkGcPolicy,
+    'framework+inlinestack': InlineStackFrameworkGcPolicy,
 }
-
-

Modified: pypy/branch/inline-shadowstack/pypy/translator/c/genc.py
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/translator/c/genc.py	(original)
+++ pypy/branch/inline-shadowstack/pypy/translator/c/genc.py	Mon Dec  6 11:38:36 2010
@@ -205,6 +205,8 @@
             name = self.config.translation.gctransformer
             if self.config.translation.gcrootfinder == "asmgcc":
                 name = "%s+asmgcroot" % (name,)
+            elif self.config.translation.gcrootfinder == "inlinestack":
+                name = "%s+inlinestack" % (name,)
             return gc.name_to_gcpolicy[name]
         return self.gcpolicy
 

Modified: pypy/branch/inline-shadowstack/pypy/translator/c/src/mem.h
==============================================================================
--- pypy/branch/inline-shadowstack/pypy/translator/c/src/mem.h	(original)
+++ pypy/branch/inline-shadowstack/pypy/translator/c/src/mem.h	Mon Dec  6 11:38:36 2010
@@ -9,6 +9,18 @@
 extern long pypy_asm_stackwalk(void*, void*);
 #define __gcnoreorderhack __gcmapend
 
+struct pypy_stackref_s {
+  struct pypy_stackref_s *prev;
+  long bitfield;
+};
+extern struct pypy_stackref_s *pypy_stackref;
+extern void *get_pypy_stackref(void);
+
+#ifndef PYPY_NOT_MAIN_FILE
+struct pypy_stackref_s *pypy_stackref = NULL;
+void *get_pypy_stackref(void) { return pypy_stackref; }
+#endif
+
 /* The following pseudo-instruction is used by --gcrootfinder=asmgcc
    just after a call to tell gcc to put a GCROOT mark on each gc-pointer
    local variable.  All such local variables need to go through a "v =



More information about the Pypy-commit mailing list