[pypy-commit] pypy stm-gc: Add a separate phase pre-inserting 'stm_writebarrier'. This phase

arigo noreply at buildbot.pypy.org
Thu Feb 16 17:48:19 CET 2012


Author: Armin Rigo <arigo at tunes.org>
Branch: stm-gc
Changeset: r52555:becc7ce5e90c
Date: 2012-02-16 17:48 +0100
http://bitbucket.org/pypy/pypy/changeset/becc7ce5e90c/

Log:	Add a separate phase pre-inserting 'stm_writebarrier'. This phase
	should insert enough write barriers for all cases. The idea is to
	to localtracking on this, and then kill the write barriers that are
	found to apply on already-local objects.

diff --git a/pypy/translator/stm/test/test_transform.py b/pypy/translator/stm/test/test_transform.py
--- a/pypy/translator/stm/test/test_transform.py
+++ b/pypy/translator/stm/test/test_transform.py
@@ -1,268 +1,41 @@
-import py
-py.test.skip("rewrite or kill")
-from pypy.rpython.lltypesystem import lltype, llmemory, rstr
-from pypy.rpython.test.test_llinterp import get_interpreter
+from pypy.translator.stm.transform import STMTransformer
+from pypy.translator.stm.transform import pre_insert_stm_writebarrier
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.conftest import option
 from pypy.objspace.flow.model import summary
-from pypy.translator.stm.llstminterp import eval_stm_graph
-from pypy.translator.stm.transform import transform_graph
-from pypy.conftest import option
 
 
-def eval_stm_func(func, arguments, stm_mode="regular_transaction",
-                  final_stm_mode="regular_transaction"):
-    interp, graph = get_interpreter(func, arguments)
-    transform_graph(graph)
-    #if option.view:
-    #    graph.show()
-    return eval_stm_graph(interp, graph, arguments, stm_mode=stm_mode,
-                          final_stm_mode=final_stm_mode,
-                          automatic_promotion=True)
+def get_graph(func, sig):
+    t = TranslationContext()
+    t.buildannotator().build_types(func, sig)
+    t.buildrtyper().specialize()
+    if option.view:
+        t.view()
+    return graphof(t, func)
 
-# ____________________________________________________________
 
-def test_simple():
-    S = lltype.GcStruct('S', ('x', lltype.Signed))
-    p = lltype.malloc(S, immortal=True)
-    p.x = 42
-    def func(p):
-        return p.x
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_getfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-    assert res == 42
-
-def test_setfield():
-    S = lltype.GcStruct('S', ('x', lltype.Signed))
-    p = lltype.malloc(S, immortal=True)
-    p.x = 42
-    def func(p):
-        p.x = 43
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_setfield': 1}
-    eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-
-def test_immutable_field():
-    S = lltype.GcStruct('S', ('x', lltype.Signed), hints = {'immutable': True})
-    p = lltype.malloc(S, immortal=True)
-    p.x = 42
-    def func(p):
-        return p.x
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'getfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-    assert res == 42
-
-def test_void_field():
-    S = lltype.GcStruct('S', ('v', lltype.Void))
-    p = lltype.malloc(S, immortal=True)
-    def func(p):
-        p.v = None
-        return p.v
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'getfield': 1, 'setfield': 1}
-
-def test_getarraysize():
-    A = lltype.GcArray(lltype.Signed)
-    p = lltype.malloc(A, 100, immortal=True)
-    p[42] = 666
-    def func(p):
-        return len(p)
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'getarraysize': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-    assert res == 100
-
-def test_getarrayitem():
-    A = lltype.GcArray(lltype.Signed)
-    p = lltype.malloc(A, 100, immortal=True)
-    p[42] = 666
-    def func(p):
-        return p[42]
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_getarrayitem': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-    assert res == 666
-
-def test_setarrayitem():
-    A = lltype.GcArray(lltype.Signed)
-    p = lltype.malloc(A, 100, immortal=True)
-    p[42] = 666
-    def func(p):
-        p[42] = 676
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_setarrayitem': 1}
-    eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-
-STRLIKE = lltype.GcStruct('STRLIKE',    # no 'immutable' in this version
-                          ('chars', lltype.Array(lltype.Char)))
-
-def test_getinteriorfield():
-    p = lltype.malloc(STRLIKE, 100, immortal=True)
-    p.chars[42] = 'X'
-    def func(p):
-        return p.chars[42]
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_getinteriorfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-    assert res == 'X'
-
-def test_setinteriorfield():
-    p = lltype.malloc(STRLIKE, 100, immortal=True)
-    def func(p):
-        p.chars[42] = 'Y'
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_setinteriorfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-
-def test_getstrchar():
-    p = lltype.malloc(rstr.STR, 100, immortal=True)
-    p.chars[42] = 'X'
-    def func(p):
-        return p.chars[42]
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'getinteriorfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-    assert res == 'X'
-
-def test_setstrchar():
-    p = lltype.malloc(rstr.STR, 100, immortal=True)
-    def func(p):
-        p.chars[42] = 'Y'
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'setinteriorfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction")
-
-def test_getfield_setfield_access_directly():
-    class P:
-        x = 42
-        _stm_access_directly_ = True
-    def func():
-        p = P()
-        p.x += 1
-    interp, graph = get_interpreter(func, [])
-    transform_graph(graph)
-    assert summary(graph) == {'malloc': 1, 'cast_pointer': 1,
-                              'getfield': 1, 'setfield': 3, 'int_add': 1}
-    res = eval_stm_graph(interp, graph, [], stm_mode="regular_transaction")
-
-def test_arrayitem_access_directly():
-    class P1:
+def test_pre_insert_stm_writebarrier():
+    class X:
         pass
-    class P2:
-        _stm_access_directly_ = True
-    class P3:
-        _stm_access_directly_ = True
-        _immutable_fields_ = ['lst->...']
-    for P in [P1, P2, P2]:
-        def func(n):
-            p = P()
-            p.lst = [0]
-            p.lst[0] = n
-            return p.lst[0]
-        interp, graph = get_interpreter(func, [42])
-        #
-        from pypy.translator.backendopt.inline import auto_inline_graphs
-        translator = interp.typer.annotator.translator
-        auto_inline_graphs(translator, translator.graphs, 16.0)
-        if option.view:
-            graph.show()
-        #
-        transform_graph(graph)
-        assert ('stm_getfield' in summary(graph)) == (P is P1)
-        assert ('stm_setfield' in summary(graph)) == (P is P1)
-        assert ('stm_getarrayitem' in summary(graph)) == (P is not P3)
-        #assert ('stm_setarrayitem' in summary(graph)) == (P is not P3) -- xxx
-        res = eval_stm_graph(interp, graph, [42],
-                             stm_mode="regular_transaction")
-        assert res == 42
-
-def test_setfield_freshly_allocated():
-    py.test.skip("XXX not implemented")
-    S = lltype.GcStruct('S', ('x', lltype.Signed))
-    def func(n):
-        p = lltype.malloc(S)
-        p.x = n
-    interp, graph = get_interpreter(func, [42])
-    transform_graph(graph)
-    assert summary(graph) == {'malloc': 1, 'setfield': 1}
-    res = eval_stm_graph(interp, graph, [42], stm_mode="regular_transaction")
-
-def test_unsupported_operation():
-    def func(n):
-        n += 1
+    class Y(X):
+        pass
+    class Z(X):
+        pass
+    def f1(n):
         if n > 5:
-            p = llmemory.raw_malloc(llmemory.sizeof(lltype.Signed))
-            llmemory.raw_free(p)
-        return n
-    res = eval_stm_func(func, [3], final_stm_mode="regular_transaction")
-    assert res == 4
-    res = eval_stm_func(func, [13], final_stm_mode="inevitable_transaction")
-    assert res == 14
-
-def test_supported_malloc():
-    S = lltype.GcStruct('S', ('x', lltype.Signed))   # GC structure
-    def func():
-        lltype.malloc(S)
-    eval_stm_func(func, [], final_stm_mode="regular_transaction")
-
-def test_supported_malloc_varsize():
-    A = lltype.GcArray(lltype.Signed)
-    def func():
-        lltype.malloc(A, 5)
-    eval_stm_func(func, [], final_stm_mode="regular_transaction")
-
-def test_unsupported_malloc():
-    S = lltype.Struct('S', ('x', lltype.Signed))   # non-GC structure
-    def func():
-        lltype.malloc(S, flavor='raw')
-    eval_stm_func(func, [], final_stm_mode="inevitable_transaction")
-test_unsupported_malloc.dont_track_allocations = True
-
-def test_unsupported_getfield_raw():
-    S = lltype.Struct('S', ('x', lltype.Signed))
-    p = lltype.malloc(S, immortal=True)
-    p.x = 42
-    def func(p):
-        return p.x
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_become_inevitable': 1, 'getfield': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction",
-                         final_stm_mode="inevitable_transaction")
-    assert res == 42
-
-def test_unsupported_setfield_raw():
-    S = lltype.Struct('S', ('x', lltype.Signed))
-    p = lltype.malloc(S, immortal=True)
-    p.x = 42
-    def func(p):
-        p.x = 43
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_become_inevitable': 1, 'setfield': 1}
-    eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction",
-                   final_stm_mode="inevitable_transaction")
-
-def test_unsupported_getarrayitem_raw():
-    A = lltype.Array(lltype.Signed)
-    p = lltype.malloc(A, 5, immortal=True)
-    p[3] = 42
-    def func(p):
-        return p[3]
-    interp, graph = get_interpreter(func, [p])
-    transform_graph(graph)
-    assert summary(graph) == {'stm_become_inevitable': 1, 'getarrayitem': 1}
-    res = eval_stm_graph(interp, graph, [p], stm_mode="regular_transaction",
-                         final_stm_mode="inevitable_transaction")
-    assert res == 42
+            x = Z()
+        else:
+            x = Y()
+        x.n = n
+        if n > 5:
+            assert isinstance(x, Z)
+            x.n = n + 2
+            x.sub = n + 1
+    #
+    graph = get_graph(f1, [int])
+    pre_insert_stm_writebarrier(graph)
+    # weak test: check that there are exactly two stm_writebarrier inserted.
+    # one should be for 'x.n = n', and one should cover both field assignments
+    # to the Z instance.
+    sum = summary(graph)
+    assert sum['stm_writebarrier'] == 2
diff --git a/pypy/translator/stm/transform.py b/pypy/translator/stm/transform.py
--- a/pypy/translator/stm/transform.py
+++ b/pypy/translator/stm/transform.py
@@ -34,6 +34,8 @@
     def transform(self):
         assert not hasattr(self.translator, 'stm_transformation_applied')
         self.start_log()
+        for graph in self.translator.graphs:
+            pre_insert_stm_writebarrier(graph)
         self.localtracker = StmLocalTracker(self.translator)
         for graph in self.translator.graphs:
             self.transform_graph(graph)
@@ -82,7 +84,9 @@
 
     # ----------
 
-    def transform_get(self, newoperations, op, stmopname, immutable=False):
+    # ----------
+
+    def transform_get(self, newoperations, op, stmopname):
         if op.result.concretetype is lltype.Void:
             newoperations.append(op)
             return
@@ -90,7 +94,7 @@
             turn_inevitable(newoperations, op.opname + '-raw')
             newoperations.append(op)
             return
-        if immutable:
+        if is_immutable(op):
             self.count_get_immutable += 1
             newoperations.append(op)
             return
@@ -103,7 +107,7 @@
         op1 = SpaceOperation(stmopname, op.args, op.result)
         newoperations.append(op1)
 
-    def transform_set(self, newoperations, op, immutable=False):
+    def transform_set(self, newoperations, op):
         if op.args[-1].concretetype is lltype.Void:
             newoperations.append(op)
             return
@@ -111,7 +115,7 @@
             turn_inevitable(newoperations, op.opname + '-raw')
             newoperations.append(op)
             return
-        if immutable:
+        if is_immutable(op):
             self.count_set_immutable += 1
             newoperations.append(op)
             return
@@ -128,37 +132,26 @@
         op1 = SpaceOperation('bare_' + op.opname, [v_local] + op.args[1:],
                              op.result)
         newoperations.append(op1)
+        import pdb; pdb.set_trace()
 
 
     def stt_getfield(self, newoperations, op):
-        STRUCT = op.args[0].concretetype.TO
-        immutable = STRUCT._immutable_field(op.args[1].value)
-        self.transform_get(newoperations, op, 'stm_getfield', immutable)
+        self.transform_get(newoperations, op, 'stm_getfield')
 
     def stt_setfield(self, newoperations, op):
-        STRUCT = op.args[0].concretetype.TO
-        immutable = STRUCT._immutable_field(op.args[1].value)
-        self.transform_set(newoperations, op, immutable)
+        self.transform_set(newoperations, op)
 
     def stt_getarrayitem(self, newoperations, op):
-        ARRAY = op.args[0].concretetype.TO
-        immutable = ARRAY._immutable_field()
-        self.transform_get(newoperations, op, 'stm_getarrayitem', immutable)
+        self.transform_get(newoperations, op, 'stm_getarrayitem')
 
     def stt_setarrayitem(self, newoperations, op):
-        ARRAY = op.args[0].concretetype.TO
-        immutable = ARRAY._immutable_field()
-        self.transform_set(newoperations, op, immutable)
+        self.transform_set(newoperations, op)
 
     def stt_getinteriorfield(self, newoperations, op):
-        OUTER = op.args[0].concretetype.TO
-        immutable = OUTER._immutable_interiorfield(unwraplist(op.args[1:]))
-        self.transform_get(newoperations, op, 'stm_getinteriorfield',immutable)
+        self.transform_get(newoperations, op, 'stm_getinteriorfield')
 
     def stt_setinteriorfield(self, newoperations, op):
-        OUTER = op.args[0].concretetype.TO
-        immutable = OUTER._immutable_interiorfield(unwraplist(op.args[1:-1]))
-        self.transform_set(newoperations, op, immutable)
+        self.transform_set(newoperations, op)
 
     def stt_malloc(self, newoperations, op):
         flags = op.args[1].value
@@ -196,3 +189,84 @@
             yield None    # unknown
         else:
             raise AssertionError(v)
+
+def is_immutable(op):
+    if op.opname in ('getfield', 'setfield'):
+        STRUCT = op.args[0].concretetype.TO
+        return STRUCT._immutable_field(op.args[1].value)
+    if op.opname in ('getarrayitem', 'setarrayitem'):
+        ARRAY = op.args[0].concretetype.TO
+        return ARRAY._immutable_field()
+    if op.opname == 'getinteriorfield':
+        OUTER = op.args[0].concretetype.TO
+        return OUTER._immutable_interiorfield(unwraplist(op.args[1:]))
+    if op.opname == 'setinteriorfield':
+        OUTER = op.args[0].concretetype.TO
+        return OUTER._immutable_interiorfield(unwraplist(op.args[1:-1]))
+    raise AssertionError(op)
+
+def pre_insert_stm_writebarrier(graph):
+    # put a number of 'stm_writebarrier' operations, one before each
+    # relevant 'set*'.  Then try to avoid the situation where we have
+    # one variable on which we do 'stm_writebarrier', but there are
+    # also other variables that contain the same pointer, e.g. casted
+    # to a different precise type.
+    from pypy.translator.stm.gcsource import COPIES_POINTER
+    #
+    def emit(op):
+        for v1 in op.args:
+            if v1 in renames:
+                # one argument at least is in 'renames', so we need
+                # to make a new SpaceOperation
+                args1 = [renames.get(v, v) for v in op.args]
+                op1 = SpaceOperation(op.opname, args1, op.result)
+                newoperations.append(op1)
+                return
+        # no argument is in 'renames', so we can just emit the op
+        newoperations.append(op)
+    #
+    for block in graph.iterblocks():
+        if block.operations == ():
+            continue
+        #
+        # figure out the variables on which we want an stm_writebarrier
+        copies = {}
+        wants_a_writebarrier = {}
+        for op in block.operations:
+            if op.opname in COPIES_POINTER:
+                assert len(op.args) == 1
+                copies[op.result] = op
+            elif (op.opname in ('setfield', 'setarrayitem',
+                                'setinteriorfield') and
+                  op.args[-1].concretetype is not lltype.Void and
+                  op.args[0].concretetype.TO._gckind == 'gc' and
+                  not is_immutable(op)):
+                wants_a_writebarrier.setdefault(op.args[0], op)
+        #
+        # back-propagate the write barrier locations through the cast_pointers
+        writebarrier_locations = {}
+        for v, op in wants_a_writebarrier.items():
+            while v in copies:
+                op = copies[v]
+                v = op.args[0]
+            protect = writebarrier_locations.setdefault(op, set())
+            protect.add(v)
+        #
+        # now insert the 'stm_writebarrier's
+        renames = {}      # {original-var: renamed-var}
+        newoperations = []
+        for op in block.operations:
+            locs = writebarrier_locations.get(op, None)
+            if locs:
+                for v1 in locs:
+                    if v1 not in renames:
+                        v2 = varoftype(v1.concretetype)
+                        op1 = SpaceOperation('stm_writebarrier', [v1], v2)
+                        emit(op1)
+                        renames[v1] = v2
+            emit(op)
+        #
+        if renames:
+            for link in block.exits:
+                link.args = [renames.get(v, v) for v in link.args]
+        block.operations = newoperations


More information about the pypy-commit mailing list