Commits

Armin Rigo  committed 7f33768

Kill the stmgc-c4's advanced writebarrier.py and replace it with
regular gc-generated write barriers.

  • Participants
  • Parent commits e4edfa4
  • Branches stmgc-c7

Comments (0)

Files changed (10)

File rpython/memory/gc/stmgc.py

     _alloc_flavor_ = "raw"
     inline_simple_malloc = True
     inline_simple_malloc_varsize = True
-    needs_write_barrier = True
+    needs_write_barrier = "stm"
     prebuilt_gc_objects_are_static_roots = False
     malloc_zero_filled = True
     object_minimal_size = 16
                                          typeid16, obj)
 
 
+    def can_optimize_clean_setarrayitems(self):
+        return False
+
+    def write_barrier(self, addr_struct):
+        """Should be turned into calls to stm_write() instead"""
+        dont_see_me
+
+
     def can_move(self, obj):
         """Means the reference will stay valid, except if not
         seen by the GC, then it can get collected."""

File rpython/memory/gctransform/framework.py

                 if op.args[0] in mallocvars:
                     mallocvars[op.result] = True
             elif op.opname in ("setfield", "setarrayitem", "setinteriorfield"):
-                TYPE = op.args[-1].concretetype
-                if (op.args[0] in mallocvars and
-                    isinstance(TYPE, lltype.Ptr) and
-                    TYPE.TO._gckind == "gc"):
+                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
+                    # hurt in the other cases.
                     result.add(op)
             else:
                 if collect_analyzer.analyze(op):
 
         self.write_barrier_ptr = None
         self.write_barrier_from_array_ptr = None
-        if GCClass.needs_write_barrier:
+        if GCClass.needs_write_barrier == "stm":
+            self.write_barrier_ptr = "stm"
+        elif GCClass.needs_write_barrier:
             self.write_barrier_ptr = getfn(GCClass.write_barrier.im_func,
                                            [s_gc, SomeAddress()],
                                            annmodel.s_None,
                   resultvar=op.result)
 
     def gct_gc_writebarrier(self, hop):
-        if self.write_barrier_ptr is None:   # incl. in case of stm
+        if self.write_barrier_ptr is None:
             return
         op = hop.spaceop
         v_addr = op.args[0]

File rpython/memory/gctransform/stmframework.py

             hop.genop("stm_pop_root_into", [var])
 
     def transform_generic_set(self, hop):
+        assert self.write_barrier_ptr == "stm"
         opname = hop.spaceop.opname
-        # XXX DO STUFF HERE
+        v_struct = hop.spaceop.args[0]
+        assert opname in ('setfield', 'setarrayitem', 'setinteriorfield',
+                          'raw_store')
+        if (v_struct.concretetype.TO._gckind == "gc"
+                and hop.spaceop not in self.clean_sets):
+            self.write_barrier_calls += 1
+            hop.genop("stm_write", [v_struct])
         hop.rename('bare_' + opname)
 
     def gc_header_for(self, obj, needs_hash=False):

File rpython/rtyper/lltypesystem/lloperation.py

     #  must be no gc-var access afterwards anyway)
     'stm_register_thread_local': LLOp(),
     'stm_unregister_thread_local': LLOp(),
+    'stm_write':              LLOp(),
     'stm_can_move':           LLOp(),
     'stm_allocate_tid':       LLOp(sideeffects=False, canmallocgc=True),
     'stm_get_from_obj':       LLOp(sideeffects=False),

File rpython/translator/stm/funcgen.py

 from rpython.translator.c.support import c_string_constant, cdecl
 from rpython.translator.c.node import Node, ContainerNode
 from rpython.translator.c.primitive import name_small_integer
-from rpython.rtyper.lltypesystem import llmemory
+from rpython.rtyper.lltypesystem import lltype, llmemory
 
 
 class StmHeaderOpaqueDefNode(Node):
 def stm_unregister_thread_local(funcgen, op):
     return 'stm_unregister_thread_local(&stm_thread_local);'
 
-def stm_can_move(funcop, op):
+def stm_write(funcgen, op):
+    assert isinstance(op.args[0].concretetype, lltype.Ptr)
+    assert op.args[0].concretetype.TO._gckind == 'gc'
+    arg0 = funcgen.expr(op.args[0])
+    return 'stm_write((object_t *)%s);' % (arg0,)
+
+def stm_can_move(funcgen, op):
     arg0 = funcgen.expr(op.args[0])
     result = funcgen.expr(op.result)
     return '%s = stm_can_move(%s);' % (result, arg0)

File rpython/translator/stm/inevitable.py

 from rpython.rtyper.lltypesystem import lltype, lloperation, rclass
-from rpython.translator.stm.writebarrier import is_immutable
+from rpython.translator.stm.support import is_immutable
 from rpython.flowspace.model import SpaceOperation, Constant
 from rpython.translator.unsimplify import varoftype
 

File rpython/translator/stm/support.py

+
+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]))
+    if op.opname in ('raw_load', 'raw_store'):
+        return False
+    raise AssertionError(op)

File rpython/translator/stm/test/test_writebarrier.py

-from rpython.rlib.rstm import register_invoke_around_extcall
-from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
-from rpython.rtyper.lltypesystem.lloperation import llop
-from rpython.translator.stm.test.transform_support import BaseTestTransform
-
-
-class TestTransform(BaseTestTransform):
-    do_write_barrier = True
-
-    def test_simple_read(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        x1 = lltype.malloc(X, immortal=True)
-        x1.foo = 42
-        x2 = lltype.malloc(X, immortal=True)
-        x2.foo = 81
-
-        def f1(n):
-            if n > 1:
-                return x2.foo
-            else:
-                return x1.foo
-
-        res = self.interpret(f1, [4])
-        assert res == 81
-        assert len(self.writemode) == 0
-        res = self.interpret(f1, [-5])
-        assert res == 42
-        assert len(self.writemode) == 0
-        assert self.barriers == ['I2R']
-
-    def test_array_size(self):
-        array_gc = lltype.GcArray(('z', lltype.Signed))
-        array_nongc = lltype.Array(('z', lltype.Signed))
-        Q = lltype.GcStruct('Q',
-                            ('gc', lltype.Ptr(array_gc)),
-                            ('raw', lltype.Ptr(array_nongc)))
-        q = lltype.malloc(Q, immortal=True)
-        q.gc = lltype.malloc(array_gc, n=3, flavor='gc', immortal=True)
-        q.raw = lltype.malloc(array_nongc, n=5, flavor='raw', immortal=True)
-        def f1(n):
-            if n == 1:
-                return len(q.gc)
-            else:
-                return len(q.raw)
-        res = self.interpret(f1, [1])
-        assert self.barriers == ['I2R', 'a2i']
-        res = self.interpret(f1, [0])
-        assert self.barriers == ['I2R']
-        
-        
-
-    def test_simple_read_2(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        x2 = lltype.malloc(X, immortal=True)
-        x2.foo = 81
-        null = lltype.nullptr(X)
-
-        def f1(n):
-            if n < 1:
-                p = null
-            else:
-                p = x2
-            return p.foo
-
-        res = self.interpret(f1, [4])
-        assert res == 81
-        assert len(self.writemode) == 0
-        assert self.barriers == ['I2R']
-
-    def test_simple_write(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        x1 = lltype.malloc(X, immortal=True)
-        x1.foo = 42
-
-        def f1(n):
-            x1.foo = n
-
-        self.interpret(f1, [4])
-        assert x1.foo == 4
-        assert len(self.writemode) == 1
-        assert self.barriers == ['I2V']
-
-    def test_simple_write_pointer(self):
-        T = lltype.GcStruct('T')
-        X = lltype.GcStruct('X', ('foo', lltype.Ptr(T)))
-        t1 = lltype.malloc(T, immortal=True)
-        x1 = lltype.malloc(X, immortal=True, zero=True)
-
-        def f1(n):
-            x1.foo = t1
-
-        self.interpret(f1, [4])
-        assert x1.foo == t1
-        assert len(self.writemode) == 1
-        assert self.barriers == ['I2W']
-
-    def test_multiple_reads(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed),
-                                 ('bar', lltype.Signed))
-        x1 = lltype.malloc(X, immortal=True)
-        x1.foo = 6
-        x1.bar = 7
-        x2 = lltype.malloc(X, immortal=True)
-        x2.foo = 81
-        x2.bar = -1
-
-        def f1(n):
-            if n > 1:
-                return x2.foo * x2.bar
-            else:
-                return x1.foo * x1.bar
-
-        res = self.interpret(f1, [4])
-        assert res == -81
-        assert len(self.writemode) == 0
-        assert self.barriers == ['I2R']
-
-    def test_malloc(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(n):
-            p = lltype.malloc(X)
-            p.foo = n
-
-        self.interpret(f1, [4])
-        assert len(self.writemode) == 1
-        assert self.barriers == []
-
-    def test_dont_repeat_write_barrier_after_malloc_if_not_a_ptr(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        x1 = lltype.malloc(X, immortal=True, zero=True)
-        def f1(n):
-            x1.foo = n
-            lltype.malloc(X)
-            x1.foo = x1.foo + n
-
-        self.interpret(f1, [4])
-        assert len(self.writemode) == 2
-        assert self.barriers == ['I2V']
-
-    def test_repeat_write_barrier_after_malloc(self):
-        T = lltype.GcStruct('T')
-        X = lltype.GcStruct('X', ('foo', lltype.Ptr(T)))
-        t1 = lltype.malloc(T, immortal=True)
-        t2 = lltype.malloc(T, immortal=True)
-        x1 = lltype.malloc(X, immortal=True, zero=True)
-        def f1(n):
-            x1.foo = t1
-            lltype.malloc(X)
-            x1.foo = t2
-
-        self.interpret(f1, [4])
-        assert len(self.writemode) == 2
-        assert self.barriers == ['I2W', 'V2W']
-
-    def test_repeat_read_barrier_after_malloc(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        x1 = lltype.malloc(X, immortal=True)
-        x1.foo = 6
-        def f1(n):
-            i = x1.foo
-            lltype.malloc(X)
-            i = x1.foo + i
-            return i
-
-        self.interpret(f1, [4])
-        assert len(self.writemode) == 1
-        assert self.barriers == ['I2R']
-
-    def test_write_may_alias(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(p, q):
-            x1 = p.foo
-            q.foo = 7
-            x2 = p.foo
-            return x1 * x2
-
-        x = lltype.malloc(X, immortal=True); x.foo = 6
-        y = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x, y])
-        assert res == 36
-        assert self.barriers == ['A2R', 'A2V', 'q2r']
-        res = self.interpret(f1, [x, x])
-        assert res == 42
-        assert self.barriers == ['A2R', 'A2V', 'Q2R']
-
-    def test_write_cannot_alias(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        Y = lltype.GcStruct('Y', ('foo', lltype.Signed))
-        def f1(p, q):
-            x1 = p.foo
-            q.foo = 7
-            x2 = p.foo
-            return x1 * x2
-
-        x = lltype.malloc(X, immortal=True); x.foo = 6
-        y = lltype.malloc(Y, immortal=True)
-        res = self.interpret(f1, [x, y])
-        assert res == 36
-        assert self.barriers == ['A2R', 'A2V']
-
-    def test_call_external_release_gil(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(p):
-            register_invoke_around_extcall()
-            x1 = p.foo
-            external_release_gil()
-            x2 = p.foo
-            return x1 * x2
-
-        x = lltype.malloc(X, immortal=True); x.foo = 6
-        res = self.interpret(f1, [x])
-        assert res == 36
-        assert self.barriers == ['A2R', 'I2R']
-
-    def test_call_external_any_gcobj(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(p):
-            register_invoke_around_extcall()
-            x1 = p.foo
-            external_any_gcobj()
-            x2 = p.foo
-            return x1 * x2
-
-        x = lltype.malloc(X, immortal=True); x.foo = 6
-        res = self.interpret(f1, [x])
-        assert res == 36
-        assert self.barriers == ['A2R', 'q2r']
-
-    def test_call_external_safest(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(p):
-            register_invoke_around_extcall()
-            x1 = p.foo
-            external_safest()
-            x2 = p.foo
-            return x1 * x2
-
-        x = lltype.malloc(X, immortal=True); x.foo = 6
-        res = self.interpret(f1, [x])
-        assert res == 36
-        assert self.barriers == ['A2R']
-
-    def test_pointer_compare_0(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(x):
-            return x != lltype.nullptr(X)
-        x = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x])
-        assert res == 1
-        assert self.barriers == []
-
-    def test_pointer_compare_1(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(x, y):
-            return x != y
-        x = lltype.malloc(X, immortal=True)
-        y = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x, y])
-        assert res == 1
-        assert self.barriers == ['=']
-        res = self.interpret(f1, [x, x])
-        assert res == 0
-        assert self.barriers == ['=']
-
-    def test_pointer_compare_2(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(x, y):
-            x.foo = 41
-            return x == y
-        x = lltype.malloc(X, immortal=True)
-        y = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x, y])
-        assert res == 0
-        assert self.barriers == ['A2V', '=']
-        res = self.interpret(f1, [x, x])
-        assert res == 1
-        assert self.barriers == ['A2V', '=']
-
-    def test_pointer_compare_3(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(x, y):
-            y.foo = 41
-            return x != y
-        x = lltype.malloc(X, immortal=True)
-        y = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x, y])
-        assert res == 1
-        assert self.barriers == ['A2V', '=']
-        res = self.interpret(f1, [x, x])
-        assert res == 0
-        assert self.barriers == ['A2V', '=']
-
-    def test_pointer_compare_4(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(x, y):
-            x.foo = 40
-            y.foo = 41
-            return x != y
-        x = lltype.malloc(X, immortal=True)
-        y = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x, y])
-        assert res == 1
-        assert self.barriers == ['A2V', 'A2V']
-        res = self.interpret(f1, [x, x])
-        assert res == 0
-        assert self.barriers == ['A2V', 'A2V']
-
-    def test_simple_loop(self):
-        X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        def f1(x, i):
-            while i > 0:
-                x.foo = i
-                i -= 1
-            return i
-        x = lltype.malloc(X, immortal=True)
-        res = self.interpret(f1, [x, 5])
-        assert res == 0
-        # for now we get this.  Later, we could probably optimize it
-        assert self.barriers == ['A2V', 'a2v', 'a2v', 'a2v', 'a2v']
-
-    def test_subclassing(self):
-        class X:
-            __slots__ = ['foo']
-        class Y(X):
-            pass
-        class Z(X):
-            pass
-        def f1(i):
-            if i > 5:
-                x = Y()
-                x.foo = 42
-                x.ybar = i
-            else:
-                x = Z()
-                x.foo = 815
-                x.zbar = 'A'
-            llop.debug_stm_flush_barrier(lltype.Void)
-            result = x.foo          # 1
-            if isinstance(x, Y):    # 2
-                result += x.ybar    # 3: optimized
-            return result
-
-        res = self.interpret(f1, [10])
-        assert res == 42 + 10
-        assert self.barriers == ['a2r', 'a2i']
-        res = self.interpret(f1, [-10])
-        assert res == 815
-        assert self.barriers == ['a2r', 'a2i']
-
-    def test_no_subclasses_2(self):
-        class Y(object):
-            pass
-        def handle(y):
-            y.ybar += 1
-        def make_y(i):
-            y = Y(); y.foo = 42; y.ybar = i
-            return y
-        def f1(i):
-            y = make_y(i)
-            llop.debug_stm_flush_barrier(lltype.Void)
-            prev = y.ybar          # a2r
-            handle(y)              # inside handle(): a2r, r2v
-            return prev + y.ybar   # q2r
-
-        res = self.interpret(f1, [10])
-        assert res == 21
-        assert self.barriers == ['a2r', 'a2r', 'r2v', 'q2r']
-
-    def test_subclassing_2(self):
-        class X:
-            __slots__ = ['foo']
-        class Y(X):
-            pass
-        class Z(X):
-            pass
-        def handle(y):
-            y.ybar += 1
-        def f1(i):
-            if i > 5:
-                y = Y(); y.foo = 42; y.ybar = i
-                x = y
-            else:
-                x = Z(); x.foo = 815; x.zbar = 'A'
-                y = Y(); y.foo = -13; y.ybar = i
-            llop.debug_stm_flush_barrier(lltype.Void)
-            prev = x.foo           # a2r
-            handle(y)              # inside handle(): a2r, r2v
-            return prev + x.foo    # q2r
-
-        res = self.interpret(f1, [10])
-        assert res == 84
-        assert self.barriers == ['a2r', 'a2r', 'r2v', 'q2r']
-
-    def test_subclassing_gcref(self):
-        Y = lltype.GcStruct('Y', ('foo', lltype.Signed),
-                                 ('ybar', lltype.Signed))
-        YPTR = lltype.Ptr(Y)
-        #
-        def handle(y):
-            y.ybar += 1
-        def f1(i):
-            if i > 5:
-                y = lltype.malloc(Y); y.foo = 52 - i; y.ybar = i
-                x = lltype.cast_opaque_ptr(llmemory.GCREF, y)
-            else:
-                y = lltype.nullptr(Y)
-                x = lltype.cast_opaque_ptr(llmemory.GCREF, y)
-            llop.debug_stm_flush_barrier(lltype.Void)
-            prev = lltype.cast_opaque_ptr(YPTR, x).foo           # a2r
-            handle(y)                            # inside handle(): a2r, r2v
-            return prev + lltype.cast_opaque_ptr(YPTR, x).ybar   # q2r?
-
-        res = self.interpret(f1, [10])
-        assert res == 42 + 11
-        assert self.barriers == ['a2r', 'a2r', 'r2v', 'a2r']
-        # Ideally we should get [... 'q2r'] but getting 'a2r' is not wrong
-        # either.  This is because from a GCREF the only thing we can do is
-        # cast_opaque_ptr, which is not special-cased in writebarrier.py.
-
-    def test_write_barrier_repeated(self):
-        class X:
-            pass
-        x = X()
-        x2 = X()
-        x3 = X()
-        def f1(i):
-            x.a = x2  # write barrier
-            y = X()   # malloc
-            x.a = x3  # repeat write barrier
-            return y
-
-        res = self.interpret(f1, [10])
-        assert self.barriers == ['I2W', 'V2W']
-
-    def test_read_immutable(self):
-        class Foo:
-            _immutable_ = True
-
-        def f1(n):
-            x = Foo()
-            llop.debug_stm_flush_barrier(lltype.Void)
-            if n > 1:
-                x.foo = n
-            llop.debug_stm_flush_barrier(lltype.Void)
-            return x.foo
-
-        res = self.interpret(f1, [4])
-        assert res == 4
-        assert self.barriers == ['a2v', 'a2i']
-
-    def test_read_immutable_prebuilt(self):
-        class Foo:
-            _immutable_ = True
-        x1 = Foo()
-        x1.foo = 42
-        x2 = Foo()
-        x2.foo = 81
-
-        def f1(n):
-            if n > 1:
-                return x2.foo
-            else:
-                return x1.foo
-
-        res = self.interpret(f1, [4])
-        assert res == 81
-        assert self.barriers == []
-
-    def test_isinstance(self):
-        class Base: pass
-        class A(Base): pass
-
-        def f1(n):
-            if n > 1:
-                x = Base()
-            else:
-                x = A()
-            return isinstance(x, A)
-
-        res = self.interpret(f1, [5])
-        assert res == False
-        assert self.barriers == ['a2i']
-        res = self.interpret(f1, [-5])
-        assert res == True
-        assert self.barriers == ['a2i']
-
-    def test_isinstance_gcremovetypeptr(self):
-        class Base: pass
-        class A(Base): pass
-
-        def f1(n):
-            if n > 1:
-                x = Base()
-            else:
-                x = A()
-            return isinstance(x, A)
-
-        res = self.interpret(f1, [5], gcremovetypeptr=True)
-        assert res == False
-        assert self.barriers == []
-        res = self.interpret(f1, [-5], gcremovetypeptr=True)
-        assert res == True
-        assert self.barriers == []
-
-    def test_infinite_loop_bug(self):
-        class A(object):
-            user_overridden_class = False
-
-            def stuff(self):
-                return 12.3
-
-            def immutable_unique_id(self):
-                if self.user_overridden_class:
-                    return None
-                from rpython.rlib.longlong2float import float2longlong
-                from rpython.rlib.rarithmetic import r_ulonglong
-                from rpython.rlib.rbigint import rbigint
-                real = self.stuff()
-                imag = self.stuff()
-                real_b = rbigint.fromrarith_int(float2longlong(real))
-                imag_b = rbigint.fromrarith_int(r_ulonglong(float2longlong(imag)))
-                val = real_b.lshift(64).or_(imag_b).lshift(3)
-                return val
-
-        def f():
-            return A().immutable_unique_id()
-
-        for i in range(10):
-            self.interpret(f, [], run=False)
-
-    def test_immut_barrier_before_weakref_deref(self):
-        import weakref
-        class Foo:
-            pass
-
-        def f1():
-            x = Foo()
-            w = weakref.ref(x)
-            llop.debug_stm_flush_barrier(lltype.Void)
-            return w()
-
-        self.interpret(f1, [])
-        assert self.barriers == ['a2i']
-
-    def test_llop_gc_writebarrier(self):
-        FOO = lltype.GcStruct('FOO')
-        x = lltype.malloc(FOO, immortal=True)
-        def f1():
-            llop.gc_writebarrier(lltype.Void, x)
-
-        self.interpret(f1, [])
-        assert self.barriers == ['I2W']
-
-    def test_stm_ignored_1(self):
-        from rpython.rlib.objectmodel import stm_ignored
-        class Foo:
-            bar = 0
-        x = Foo()
-        def f1():
-            with stm_ignored:
-                x.bar += 2
-
-        self.interpret(f1, [])
-        assert self.barriers == []
-
-    def test_stm_ignored_2(self):
-        from rpython.rlib.objectmodel import stm_ignored
-        class Foo:
-            bar = 0
-        def f1():
-            y = Foo()
-            llop.debug_stm_flush_barrier(lltype.Void)
-            with stm_ignored:
-                y.bar += 2
-
-        self.interpret(f1, [])
-        assert self.barriers == ['a2i']
-
-
-    def test_transaction_breaking_ops(self):
-        class X:
-            pass
-        x = X()
-        x2 = X()
-
-        def f1(f):
-            x.a = x2  # write barrier
-            llop.stm_commit_transaction(lltype.Void)
-            x.a = x2
-            llop.stm_begin_inevitable_transaction(lltype.Void)
-            x.a = x2
-            llop.stm_partial_commit_and_resume_other_threads(lltype.Void)
-            x.a = x2
-            llop.jit_assembler_call(lltype.Void)
-            x.a = x2
-            llop.stm_perform_transaction(lltype.Void)
-            x.a = x2
-            return x
-
-        self.interpret(f1, [1])
-        assert self.barriers == ['I2W']*6
-            
-
-
-external_release_gil = rffi.llexternal('external_release_gil', [], lltype.Void,
-                                       _callable=lambda: None,
-                                       random_effects_on_gcobjs=True,
-                                       releasegil=True)
-external_any_gcobj = rffi.llexternal('external_any_gcobj', [], lltype.Void,
-                                     _callable=lambda: None,
-                                     random_effects_on_gcobjs=True,
-                                     releasegil=False)
-external_safest = rffi.llexternal('external_safest', [], lltype.Void,
-                                  _callable=lambda: None,
-                                  random_effects_on_gcobjs=False,
-                                  releasegil=False)

File rpython/translator/stm/transform.py

-from rpython.translator.backendopt.writeanalyze import WriteAnalyzer
-from rpython.translator.stm.writebarrier import insert_stm_barrier
 from rpython.translator.stm.inevitable import insert_turn_inevitable
 from rpython.translator.stm.jitdriver import reorganize_around_jit_driver
 from rpython.translator.stm.threadlocalref import transform_tlref
-from rpython.translator.stm.breakfinder import TransactionBreakAnalyzer
 from rpython.translator.c.support import log
-from rpython.memory.gctransform.framework import CollectAnalyzer
 
 
 class STMTransformer(object):
 
     def __init__(self, translator):
         self.translator = translator
-        self.barrier_counts = {}
 
     def transform(self):
         assert not hasattr(self.translator, 'stm_transformation_applied')
         self.start_log()
         self.transform_jit_driver()
-        self.transform_write_barrier()
         self.transform_turn_inevitable()
         self.print_logs()
         self.translator.stm_transformation_applied = True
         self.transform_threadlocalref()
         self.print_logs_after_gc()
 
-    def transform_write_barrier(self):
-        self.write_analyzer = WriteAnalyzer(self.translator)
-        self.collect_analyzer = CollectAnalyzer(self.translator)
-        self.break_analyzer = TransactionBreakAnalyzer(self.translator)
-        for graph in self.translator.graphs:
-            insert_stm_barrier(self, graph)
-        for key, value in sorted(self.barrier_counts.items()):
-            log("%s: %d barriers" % (key, value[0]))
-        del self.write_analyzer
-        del self.collect_analyzer
-        del self.break_analyzer
-
     def transform_turn_inevitable(self):
         for graph in self.translator.graphs:
             insert_turn_inevitable(graph)

File rpython/translator/stm/writebarrier.py

-from rpython.flowspace.model import SpaceOperation, Constant, Variable
-from rpython.translator.unsimplify import varoftype, insert_empty_block
-from rpython.translator.unsimplify import insert_empty_startblock
-from rpython.rtyper.lltypesystem import lltype
-from rpython.translator.backendopt.writeanalyze import top_set
-from rpython.translator.simplify import join_blocks
-
-
-MALLOCS = set([
-    'malloc', 'malloc_varsize',
-    'malloc_nonmovable', 'malloc_nonmovable_varsize',
-    ])
-
-def unwraplist(list_v):
-    for v in list_v: 
-        if isinstance(v, Constant):
-            yield v.value
-        elif isinstance(v, Variable):
-            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]))
-    if op.opname in ('raw_load', 'raw_store'):
-        return False
-    raise AssertionError(op)
-
-def needs_barrier(frm, to):
-    return to > frm
-
-def is_gc_ptr(T):
-    return isinstance(T, lltype.Ptr) and T.TO._gckind == 'gc'
-
-
-class Renaming(object):
-    def __init__(self, newvar, category):
-        self.newvar = newvar        # a Variable or a Constant
-        self.TYPE = newvar.concretetype
-        self.category = category
-
-
-class BlockTransformer(object):
-
-    def __init__(self, stmtransformer, block):
-        self.stmtransformer = stmtransformer
-        self.block = block
-        self.patch = None
-        self.inputargs_category = None
-        self.inputargs_category_per_link = {}
-
-    def init_start_block(self):
-        from_outside = ['A'] * len(self.block.inputargs)
-        self.inputargs_category_per_link[None] = from_outside
-        self.update_inputargs_category()
-
-
-    def analyze_inside_block(self, graph):
-        gcremovetypeptr = (
-            self.stmtransformer.translator.config.translation.gcremovetypeptr)
-        wants_a_barrier = {}
-        expand_comparison = set()
-        stm_ignored = False
-        for op in self.block.operations:
-            is_getter = (op.opname in ('getfield', 'getarrayitem',
-                                       'getinteriorfield', 'raw_load') and
-                         op.result.concretetype is not lltype.Void and
-                         is_gc_ptr(op.args[0].concretetype))
-
-            if (gcremovetypeptr and op.opname in ('getfield', 'setfield') and
-                op.args[1].value == 'typeptr' and
-                op.args[0].concretetype.TO._hints.get('typeptr')):
-                # if gcremovetypeptr, we can access directly the typeptr
-                # field even on a stub
-                pass
-
-            elif (op.opname in ('getarraysize', 'getinteriorarraysize') and
-                  is_gc_ptr(op.args[0].concretetype)):
-                # XXX: or (is_getter and is_immutable(op))):
-                # we can't leave getarraysize or the immutable getfields
-                # fully unmodified: we need at least immut_read_barrier
-                # to detect stubs.
-                wants_a_barrier[op] = 'I'
-
-            elif is_getter:
-                # the non-immutable getfields need a regular read barrier
-                wants_a_barrier[op] = 'R'
-
-            elif (op.opname in ('setfield', 'setarrayitem',
-                                'setinteriorfield', 'raw_store') and
-                  op.args[-1].concretetype is not lltype.Void and
-                  is_gc_ptr(op.args[0].concretetype)):
-                # setfields need a regular write barrier
-                T = op.args[-1].concretetype
-                if is_gc_ptr(T):
-                    wants_a_barrier[op] = 'W'
-                else:
-                    # a write of a non-gc pointer doesn't need to check for
-                    # the GCFLAG_WRITEBARRIER
-                    wants_a_barrier[op] = 'V'
-
-            elif (op.opname in ('ptr_eq', 'ptr_ne') and
-                  is_gc_ptr(op.args[0].concretetype)):
-                # GC pointer comparison might need special care
-                expand_comparison.add(op)
-
-            elif op.opname == 'weakref_deref':
-                # 'weakref_deref' needs an immutable read barrier
-                wants_a_barrier[op] = 'I'
-
-            elif op.opname == 'gc_writebarrier':
-                wants_a_barrier[op] = 'W'
-
-            elif op.opname == 'stm_ignored_start':
-                assert not stm_ignored, "nested 'with stm_ignored'"
-                stm_ignored = True
-
-            elif op.opname == 'stm_ignored_stop':
-                assert stm_ignored, "stm_ignored_stop without start?"
-                stm_ignored = False
-
-            if stm_ignored and op in wants_a_barrier:
-                if wants_a_barrier[op] == 'W':
-                    raise Exception(
-                        "%r: 'with stm_ignored:' contains unsupported "
-                        "operation %r writing a GC pointer" % (graph, op))
-                if wants_a_barrier[op] == 'R' and is_getter and (
-                        is_gc_ptr(op.result.concretetype)):
-                    raise Exception(
-                        "%r: 'with stm_ignored:' contains unsupported "
-                        "operation %r reading a GC pointer" % (graph, op))
-                assert 'I' <= wants_a_barrier[op] < 'W'
-                wants_a_barrier[op] = 'I'
-        #
-        if stm_ignored:
-            raise Exception("%r: 'with stm_ignored:' code body too complex"
-                            % (graph,))
-        self.wants_a_barrier = wants_a_barrier
-        self.expand_comparison = expand_comparison
-
-
-    def flow_through_block(self, graphinfo):
-
-        def renfetch(v):
-            try:
-                return renamings[v]
-            except KeyError:
-                if isinstance(v, Variable):
-                    ren = Renaming(v, 'A')
-                else:
-                    ren = Renaming(v, 'I')  # prebuilt objects cannot be stubs
-                renamings[v] = ren
-                return ren
-
-        def get_category_or_null(v):
-            # 'v' is an original variable here, or a constant
-            if isinstance(v, Constant) and not v.value:    # a NULL constant
-                return 'Z'
-            if v in renamings:
-                return renamings[v].category
-            if isinstance(v, Constant):
-                return 'I'
-            else:
-                return 'A'
-
-        def renamings_get(v):
-            try:
-                ren = renamings[v]
-            except KeyError:
-                return v       # unmodified
-            v2 = ren.newvar
-            if v2.concretetype == v.concretetype:
-                return v2
-            v3 = varoftype(v.concretetype)
-            newoperations.append(SpaceOperation('cast_pointer', [v2], v3))
-            if lltype.castable(ren.TYPE, v3.concretetype) > 0:
-                ren.TYPE = v3.concretetype
-            return v3
-
-        # note: 'renamings' maps old vars to new vars, but cast_pointers
-        # are done lazily.  It means that the two vars may not have
-        # exactly the same type.
-        renamings = {}   # {original-var: Renaming(newvar, category)}
-        newoperations = []
-        stmtransformer = self.stmtransformer
-
-        # make the initial trivial renamings needed to have some precise
-        # categories for the input args
-        for v, cat in zip(self.block.inputargs, self.inputargs_category):
-            if is_gc_ptr(v.concretetype):
-                assert cat is not None
-                renamings[v] = Renaming(v, cat)
-
-        for op in self.block.operations:
-            #
-            if (op.opname in ('cast_pointer', 'same_as') and
-                    is_gc_ptr(op.result.concretetype)):
-                renamings[op.result] = renfetch(op.args[0])
-                continue
-            #
-            to = self.wants_a_barrier.get(op)
-            if to is not None:
-                ren = renfetch(op.args[0])
-                frm = ren.category
-                if needs_barrier(frm, to):
-                    try:
-                        b = stmtransformer.barrier_counts[frm, to]
-                    except KeyError:
-                        c_info = Constant('%s2%s' % (frm, to), lltype.Void)
-                        b = [0, c_info]
-                        stmtransformer.barrier_counts[frm, to] = b
-                    b[0] += 1
-                    c_info = b[1]
-                    v = ren.newvar
-                    w = varoftype(v.concretetype)
-                    newop = SpaceOperation('stm_barrier', [c_info, v], w)
-                    newoperations.append(newop)
-                    ren.newvar = w
-                    ren.category = to
-            #
-            newop = SpaceOperation(op.opname,
-                                   [renamings_get(v) for v in op.args],
-                                   op.result)
-            newoperations.append(newop)
-            #
-            if op in self.expand_comparison:
-                cats = (get_category_or_null(op.args[0]),
-                        get_category_or_null(op.args[1]))
-                if 'Z' not in cats and (cats[0] < 'V' or cats[1] < 'V'):
-                    if newop.opname == 'ptr_ne':
-                        v = varoftype(lltype.Bool)
-                        negop = SpaceOperation('bool_not', [v],
-                                               newop.result)
-                        newoperations.append(negop)
-                        newop.result = v
-                    newop.opname = 'stm_ptr_eq'
-
-            if stmtransformer.break_analyzer.analyze(op):
-                # this operation can perform a transaction break:
-                # all pointers are lowered to 'I', because a non-
-                # stub cannot suddenly point to a stub, but we
-                # cannot guarantee anything more
-                for ren in renamings.values():
-                    if ren.category > 'I':
-                        ren.category = 'I'
-
-            if op.opname == 'debug_stm_flush_barrier':
-                for ren in renamings.values():
-                    ren.category = 'A'
-
-            if stmtransformer.collect_analyzer.analyze(op):
-                # this operation can collect: we bring all 'W'
-                # categories back to 'V', because we would need
-                # a repeat_write_barrier on them afterwards
-                for ren in renamings.values():
-                    if ren.category == 'W':
-                        ren.category = 'V'
-
-            effectinfo = stmtransformer.write_analyzer.analyze(
-                op, graphinfo=graphinfo)
-            if effectinfo:
-                if effectinfo is top_set:
-                    # this operation can perform random writes: any
-                    # 'R'-category object falls back to 'Q' because
-                    # we would need a repeat_read_barrier()
-                    for ren in renamings.values():
-                        if ren.category == 'R':
-                            ren.category = 'Q'
-                else:
-                    # the same, but only on objects of the right types
-                    # -- we need to consider 'types' or any base type
-                    types = set()
-                    for entry in effectinfo:
-                        TYPE = entry[1].TO
-                        while TYPE is not None:
-                            types.add(TYPE)
-                            if not isinstance(TYPE, lltype.Struct):
-                                break
-                            _, TYPE = TYPE._first_struct()
-                    for ren in renamings.values():
-                        if ren.TYPE.TO in types and ren.category == 'R':
-                            ren.category = 'Q'
-
-            if op.opname in MALLOCS:
-                assert op.result not in renamings
-                renamings[op.result] = Renaming(op.result, 'W')
-
-        if isinstance(self.block.exitswitch, Variable):
-            switchv = renamings_get(self.block.exitswitch)
-        else:
-            switchv = None
-        blockoperations = newoperations
-        linkoperations = []
-        for link in self.block.exits:
-            output_categories = []
-            for v in link.args:
-                if is_gc_ptr(v.concretetype):
-                    cat = get_category_or_null(v)
-                else:
-                    cat = None
-                output_categories.append(cat)
-            newoperations = []
-            newargs = [renamings_get(v) for v in link.args]
-            linkoperations.append((newargs, newoperations, output_categories))
-        #
-        # Record how we'd like to patch the block, but don't do any
-        # patching yet
-        self.patch = (blockoperations, switchv, linkoperations)
-
-
-    def update_targets(self, block_transformers):
-        (_, _, linkoperations) = self.patch
-        assert len(linkoperations) == len(self.block.exits)
-        targetbts = []
-        for link, (_, _, output_categories) in zip(self.block.exits,
-                                                   linkoperations):
-            targetblock = link.target
-            if targetblock not in block_transformers:
-                continue      # ignore the exit block
-            targetbt = block_transformers[targetblock]
-            targetbt.inputargs_category_per_link[link] = output_categories
-            if targetbt.update_inputargs_category():
-                targetbts.append(targetbt)
-        return set(targetbts)
-
-    def update_inputargs_category(self):
-        values = self.inputargs_category_per_link.values()
-        newcats = []
-        for i, v in enumerate(self.block.inputargs):
-            if is_gc_ptr(v.concretetype):
-                cats = [output_categories[i] for output_categories in values]
-                assert None not in cats
-                newcats.append(min(cats))
-            else:
-                newcats.append(None)
-        if newcats != self.inputargs_category:
-            self.inputargs_category = newcats
-            return True
-        else:
-            return False
-
-
-    def patch_now(self):
-        if self.patch is None:
-            return
-        newoperations, switchv, linkoperations = self.patch
-        self.block.operations = newoperations
-        if switchv is not None:
-            self.block.exitswitch = switchv
-        assert len(linkoperations) == len(self.block.exits)
-        for link, (newargs, newoperations, _) in zip(self.block.exits,
-                                                     linkoperations):
-            link.args[:] = newargs
-            if newoperations:
-                # must put them in a fresh block along the link
-                annotator = self.stmtransformer.translator.annotator
-                newblock = insert_empty_block(annotator, link,
-                                              newoperations)
-
-
-def insert_stm_barrier(stmtransformer, graph):
-    return #XXX
-
-    """This function uses the following characters for 'categories':
-
-           * 'A': any general pointer
-           * 'I': not a stub (immut_read_barrier was applied)
-           * 'Q': same as R, except needs a repeat_read_barrier
-           * 'R': the read barrier was applied
-           * 'V': same as W, except needs a repeat_write_barrier
-           * 'W': the write barrier was applied
-           * 'Z': the null constant
-
-       The letters are chosen so that a barrier is needed to change a
-       pointer from category x to category y if and only if y > x.
-    """
-    join_blocks(graph)
-    graphinfo = stmtransformer.write_analyzer.compute_graph_info(graph)
-    annotator = stmtransformer.translator.annotator
-    insert_empty_startblock(annotator, graph)
-
-    block_transformers = {}
-
-    for block in graph.iterblocks():
-        if block.operations == ():
-            continue
-        bt = BlockTransformer(stmtransformer, block)
-        bt.analyze_inside_block(graph)
-        block_transformers[block] = bt
-
-    bt = block_transformers[graph.startblock]
-    bt.init_start_block()
-    pending = set([bt])
-
-    while pending:
-        bt = pending.pop()
-        bt.flow_through_block(graphinfo)
-        pending |= bt.update_targets(block_transformers)
-
-    for bt in block_transformers.values():
-        bt.patch_now()