Author: Remi Meier <[email protected]>
Branch: stmgc-c4
Changeset: r66391:f37582685aca
Date: 2013-08-28 15:07 +0200
http://bitbucket.org/pypy/pypy/changeset/f37582685aca/

Log:    merge static-write-barriers

diff --git a/rpython/jit/backend/tool/viewcode.py 
b/rpython/jit/backend/tool/viewcode.py
--- a/rpython/jit/backend/tool/viewcode.py
+++ b/rpython/jit/backend/tool/viewcode.py
@@ -58,6 +58,9 @@
         'arm': 'arm',
         'arm_32': 'arm',
     }
+    backend_to_machine = {
+        'x86-64': 'i386:x86-64',
+    }
     cmd = find_objdump()
     objdump = ('%(command)s -w -M %(backend)s -b binary -m %(machine)s '
                '--disassembler-options=intel-mnemonics '
@@ -66,12 +69,13 @@
     f = open(tmpfile, 'wb')
     f.write(data)
     f.close()
+    backend = objdump_backend_option[backend_name]
     p = subprocess.Popen(objdump % {
         'command': cmd,
         'file': tmpfile,
         'origin': originaddr,
-        'backend': objdump_backend_option[backend_name],
-        'machine': 'i386' if not backend_name.startswith('arm') else 'arm',
+        'backend': backend,
+        'machine': backend_to_machine.get(backend, backend),
     }, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
     stdout, stderr = p.communicate()
     assert not p.returncode, ('Encountered an error running objdump: %s' %
@@ -239,7 +243,7 @@
         self.backend_name = None
         self.executable_name = None
 
-    def parse(self, f, textonly=True, truncate_addr=True):
+    def parse(self, f, textonly=True):
         for line in f:
             line = line[line.find('#') + 1:].strip()
             if line.startswith('BACKEND '):
@@ -251,9 +255,7 @@
                 if len(pieces) == 3:
                     continue     # empty line
                 baseaddr = long(pieces[1][1:], 16)
-                if truncate_addr:
-                    baseaddr &= 0xFFFFFFFFL
-                elif baseaddr < 0:
+                if baseaddr < 0:
                     baseaddr += (2 * sys.maxint + 2)
                 offset = int(pieces[2][1:])
                 addr = baseaddr + offset
@@ -273,9 +275,7 @@
                 assert pieces[1].startswith('@')
                 assert pieces[2].startswith('+')
                 baseaddr = long(pieces[1][1:], 16)
-                if truncate_addr:
-                    baseaddr &= 0xFFFFFFFFL
-                elif baseaddr < 0:
+                if baseaddr < 0:
                     baseaddr += (2 * sys.maxint + 2)
                 offset = int(pieces[2][1:])
                 addr = baseaddr + offset
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
@@ -624,6 +624,7 @@
     'debug_reraise_traceback': LLOp(),
     'debug_print_traceback':   LLOp(),
     'debug_nonnull_pointer':   LLOp(canrun=True),
+    'debug_stm_flush_barrier': LLOp(canrun=True),
 
     # __________ instrumentation _________
     'instrument_count':     LLOp(),
diff --git a/rpython/rtyper/lltypesystem/opimpl.py 
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -673,6 +673,9 @@
 def op_nop(x):
     pass
 
+def op_debug_stm_flush_barrier():
+    pass
+
 # ____________________________________________________________
 
 def get_op_impl(opname):
diff --git a/rpython/translator/stm/breakfinder.py 
b/rpython/translator/stm/breakfinder.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/stm/breakfinder.py
@@ -0,0 +1,22 @@
+from rpython.translator.backendopt import graphanalyze
+from rpython.translator.simplify import get_funcobj
+
+
+TRANSACTION_BREAK = set([
+    'stm_commit_transaction',
+    'stm_begin_inevitable_transaction',
+    'stm_perform_transaction',
+    ])
+
+
+class TransactionBreakAnalyzer(graphanalyze.BoolGraphAnalyzer):
+
+    def analyze_simple_operation(self, op, graphinfo):
+        return op.opname in TRANSACTION_BREAK
+
+    def analyze_external_call(self, op, seen=None):
+        # if 'funcobj' releases the GIL, then the GIL-releasing
+        # functions themselves will call stm_commit_transaction
+        # and stm_begin_inevitable_transaction.  This case is
+        # covered above.
+        return False
diff --git a/rpython/translator/stm/funcgen.py 
b/rpython/translator/stm/funcgen.py
--- a/rpython/translator/stm/funcgen.py
+++ b/rpython/translator/stm/funcgen.py
@@ -48,19 +48,27 @@
 def stm_finalize(funcgen, op):
     return 'stm_finalize();'
 
-_STM_BARRIER_FUNCS = {   # XXX try to see if some combinations can be shorter
-    'P2R': 'stm_read_barrier',
-    'G2R': 'stm_read_barrier',
-    'O2R': 'stm_read_barrier',
-    'P2W': 'stm_write_barrier',
-    'G2W': 'stm_write_barrier',
-    'O2W': 'stm_write_barrier',
-    'R2W': 'stm_write_barrier',
-    }
-
 def stm_barrier(funcgen, op):
     category_change = op.args[0].value
-    funcname = _STM_BARRIER_FUNCS[category_change]
+    frm, middle, to = category_change
+    assert middle == '2'
+    assert frm < to
+    if to == 'W':
+        if frm >= 'V':
+            funcname = 'stm_repeat_write_barrier'
+        else:
+            funcname = 'stm_write_barrier'
+    elif to == 'V':
+        funcname = 'stm_write_barrier_noptr'
+    elif to == 'R':
+        if frm >= 'Q':
+            funcname = 'stm_repeat_read_barrier'
+        else:
+            funcname = 'stm_read_barrier'
+    elif to == 'I':
+        funcname = 'stm_immut_read_barrier'
+    else:
+        raise AssertionError(category_change)
     assert op.args[1].concretetype == op.result.concretetype
     arg = funcgen.expr(op.args[1])
     result = funcgen.expr(op.result)
@@ -69,11 +77,20 @@
         funcname, arg)
 
 def stm_ptr_eq(funcgen, op):
-    arg0 = funcgen.expr(op.args[0])
-    arg1 = funcgen.expr(op.args[1])
+    args = [funcgen.expr(v) for v in op.args]
     result = funcgen.expr(op.result)
+    # check for prebuilt arguments
+    for i, j in [(0, 1), (1, 0)]:
+        if isinstance(op.args[j], Constant):
+            if op.args[j].value:     # non-NULL
+                return ('%s = stm_pointer_equal_prebuilt((gcptr)%s, 
(gcptr)%s);'
+                        % (result, args[i], args[j]))
+            else:
+                # this case might be unreachable, but better safe than sorry
+                return '%s = (%s == NULL);' % (result, args[i])
+    #
     return '%s = stm_pointer_equal((gcptr)%s, (gcptr)%s);' % (
-        result, arg0, arg1)
+        result, args[0], args[1])
 
 def stm_become_inevitable(funcgen, op):
     try:
diff --git a/rpython/translator/stm/src_stm/revision 
b/rpython/translator/stm/src_stm/revision
--- a/rpython/translator/stm/src_stm/revision
+++ b/rpython/translator/stm/src_stm/revision
@@ -1,1 +1,1 @@
-63c2673c2045
+cdd017855adc+
diff --git a/rpython/translator/stm/test/test_writebarrier.py 
b/rpython/translator/stm/test/test_writebarrier.py
--- a/rpython/translator/stm/test/test_writebarrier.py
+++ b/rpython/translator/stm/test/test_writebarrier.py
@@ -1,4 +1,6 @@
-from rpython.rtyper.lltypesystem import lltype, rffi
+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
 
 
@@ -24,7 +26,7 @@
         res = self.interpret(f1, [-5])
         assert res == 42
         assert len(self.writemode) == 0
-        assert self.barriers == ['P2R']
+        assert self.barriers == ['I2R']
 
     def test_simple_write(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -37,7 +39,21 @@
         self.interpret(f1, [4])
         assert x1.foo == 4
         assert len(self.writemode) == 1
-        assert self.barriers == ['P2W']
+        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),
@@ -58,7 +74,7 @@
         res = self.interpret(f1, [4])
         assert res == -81
         assert len(self.writemode) == 0
-        assert self.barriers == ['P2R']
+        assert self.barriers == ['I2R']
 
     def test_malloc(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -70,10 +86,9 @@
         assert len(self.writemode) == 1
         assert self.barriers == []
 
-    def test_repeat_write_barrier_after_malloc(self):
+    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)
-        x1.foo = 6
+        x1 = lltype.malloc(X, immortal=True, zero=True)
         def f1(n):
             x1.foo = n
             lltype.malloc(X)
@@ -81,7 +96,22 @@
 
         self.interpret(f1, [4])
         assert len(self.writemode) == 2
-        assert self.barriers == ['P2W', 'r2w']
+        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))
@@ -95,7 +125,7 @@
 
         self.interpret(f1, [4])
         assert len(self.writemode) == 1
-        assert self.barriers == ['P2R']
+        assert self.barriers == ['I2R']
 
     def test_write_may_alias(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -109,10 +139,10 @@
         y = lltype.malloc(X, immortal=True)
         res = self.interpret(f1, [x, y])
         assert res == 36
-        assert self.barriers == ['P2R', 'P2W', 'p2r']
+        assert self.barriers == ['A2R', 'A2V', 'q2r']
         res = self.interpret(f1, [x, x])
         assert res == 42
-        assert self.barriers == ['P2R', 'P2W', 'P2R']
+        assert self.barriers == ['A2R', 'A2V', 'Q2R']
 
     def test_write_cannot_alias(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -127,37 +157,49 @@
         y = lltype.malloc(Y, immortal=True)
         res = self.interpret(f1, [x, y])
         assert res == 36
-        assert self.barriers == ['P2R', 'P2W']
+        assert self.barriers == ['A2R', 'A2V']
 
-    def test_call_external_random_effects(self):
+    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_stuff()
+            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 == ['P2R', 'p2r']
+        assert self.barriers == ['A2R', 'I2R']
 
-    def test_call_external_no_random_effects(self):
+    def test_call_external_any_gcobj(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
-        external_stuff = rffi.llexternal('external_stuff2', [], lltype.Void,
-                                         _callable=lambda: None,
-                                         random_effects_on_gcobjs=False,
-                                         threadsafe=False)
         def f1(p):
+            register_invoke_around_extcall()
             x1 = p.foo
-            external_stuff()
+            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 == ['P2R']
+        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))
@@ -190,10 +232,10 @@
         y = lltype.malloc(X, immortal=True)
         res = self.interpret(f1, [x, y])
         assert res == 0
-        assert self.barriers == ['P2W', '=']
+        assert self.barriers == ['A2V', '=']
         res = self.interpret(f1, [x, x])
         assert res == 1
-        assert self.barriers == ['P2W', '=']
+        assert self.barriers == ['A2V', '=']
 
     def test_pointer_compare_3(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -204,10 +246,10 @@
         y = lltype.malloc(X, immortal=True)
         res = self.interpret(f1, [x, y])
         assert res == 1
-        assert self.barriers == ['P2W', '=']
+        assert self.barriers == ['A2V', '=']
         res = self.interpret(f1, [x, x])
         assert res == 0
-        assert self.barriers == ['P2W', '=']
+        assert self.barriers == ['A2V', '=']
 
     def test_pointer_compare_4(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -219,10 +261,10 @@
         y = lltype.malloc(X, immortal=True)
         res = self.interpret(f1, [x, y])
         assert res == 1
-        assert self.barriers == ['P2W', 'P2W']
+        assert self.barriers == ['A2V', 'A2V']
         res = self.interpret(f1, [x, x])
         assert res == 0
-        assert self.barriers == ['P2W', 'P2W']
+        assert self.barriers == ['A2V', 'A2V']
 
     def test_simple_loop(self):
         X = lltype.GcStruct('X', ('foo', lltype.Signed))
@@ -235,7 +277,7 @@
         res = self.interpret(f1, [x, 5])
         assert res == 0
         # for now we get this.  Later, we could probably optimize it
-        assert self.barriers == ['P2W', 'p2w', 'p2w', 'p2w', 'p2w']
+        assert self.barriers == ['A2V', 'a2v', 'a2v', 'a2v', 'a2v']
 
     def test_subclassing(self):
         class X:
@@ -253,35 +295,210 @@
                 x = Z()
                 x.foo = 815
                 x.zbar = 'A'
-            external_stuff()
+            llop.debug_stm_flush_barrier(lltype.Void)
             result = x.foo          # 1
             if isinstance(x, Y):    # 2
-                result += x.ybar    # 3
+                result += x.ybar    # 3: optimized
             return result
 
         res = self.interpret(f1, [10])
         assert res == 42 + 10
-        assert self.barriers == ['p2r', 'p2r', 'p2r'] # from 3 blocks (could be
-                                                      # optimized later)
+        assert self.barriers == ['a2r', 'a2i']
         res = self.interpret(f1, [-10])
         assert res == 815
-        assert self.barriers == ['p2r', 'p2r']
+        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 = i   # write barrier
+            x.a = x2  # write barrier
             y = X()   # malloc
-            x.a += 1  # write barrier again
+            x.a = x3  # repeat write barrier
             return y
 
         res = self.interpret(f1, [10])
-        assert self.barriers == ['P2W', 'r2w']
+        assert self.barriers == ['I2W', 'V2W']
 
+    def test_read_immutable(self):
+        class Foo:
+            _immutable_ = True
 
-external_stuff = rffi.llexternal('external_stuff', [], lltype.Void,
-                                 _callable=lambda: None,
-                                 random_effects_on_gcobjs=True,
-                                 threadsafe=False)
+        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)
+
+
+external_release_gil = rffi.llexternal('external_release_gil', [], lltype.Void,
+                                       _callable=lambda: None,
+                                       random_effects_on_gcobjs=True,
+                                       threadsafe=True)   # GIL is released
+external_any_gcobj = rffi.llexternal('external_any_gcobj', [], lltype.Void,
+                                     _callable=lambda: None,
+                                     random_effects_on_gcobjs=True,
+                                     threadsafe=False)   # GIL is not released
+external_safest = rffi.llexternal('external_safest', [], lltype.Void,
+                                  _callable=lambda: None,
+                                  random_effects_on_gcobjs=False,
+                                  threadsafe=False)   # GIL is not released
diff --git a/rpython/translator/stm/test/test_ztranslated.py 
b/rpython/translator/stm/test/test_ztranslated.py
--- a/rpython/translator/stm/test/test_ztranslated.py
+++ b/rpython/translator/stm/test/test_ztranslated.py
@@ -1,8 +1,6 @@
-import py
 from rpython.rlib import rstm, rgc, objectmodel
-from rpython.rtyper.lltypesystem import lltype, llmemory, rffi, rclass
+from rpython.rtyper.lltypesystem import lltype, rffi
 from rpython.rtyper.lltypesystem.lloperation import llop
-from rpython.rtyper.annlowlevel import cast_instance_to_base_ptr
 from rpython.translator.stm.test.support import CompiledSTMTests
 from rpython.translator.stm.test import targetdemo2
 
@@ -303,3 +301,31 @@
         t, cbuilder = self.compile(main)
         data = cbuilder.cmdexec('a b')
         assert 'test ok\n' in data
+
+    def test_stm_pointer_equal(self):
+        class Foo:
+            pass
+        prebuilt_foo = Foo()
+        def make(n):
+            foo1 = Foo()
+            foo2 = Foo()
+            if n < 100:
+                return foo1, foo2, foo1, None
+            return None, None, None, foo1     # to annotate as "can be none"
+        def main(argv):
+            foo1, foo2, foo3, foo4 = make(len(argv))
+            assert foo1 is not prebuilt_foo
+            assert foo1 is not foo2
+            assert foo1 is foo3
+            assert foo4 is None
+            assert foo1 is not None
+            assert prebuilt_foo is not foo1
+            assert None is not foo1
+            assert None is foo4
+            print 'test ok'
+            return 0
+
+        main([])
+        t, cbuilder = self.compile(main)
+        data = cbuilder.cmdexec('')
+        assert 'test ok\n' in data
diff --git a/rpython/translator/stm/test/transform_support.py 
b/rpython/translator/stm/test/transform_support.py
--- a/rpython/translator/stm/test/transform_support.py
+++ b/rpython/translator/stm/test/transform_support.py
@@ -2,7 +2,7 @@
 from rpython.rtyper.llinterp import LLFrame
 from rpython.rtyper.test.test_llinterp import get_interpreter, clear_tcache
 from rpython.translator.stm.transform import STMTransformer
-from rpython.translator.stm.writebarrier import NEEDS_BARRIER
+from rpython.translator.stm.writebarrier import needs_barrier
 from rpython.conftest import option
 
 
@@ -33,12 +33,12 @@
         if isinstance(p, _stmptr):
             return p._category
         if not p:
-            return 'N'
+            return None
         if p._solid:
-            return 'P'     # allocated with immortal=True
+            return 'I'     # allocated with immortal=True
         raise AssertionError("unknown category on %r" % (p,))
 
-    def interpret(self, fn, args):
+    def interpret(self, fn, args, gcremovetypeptr=False, run=True):
         self.build_state()
         clear_tcache()
         interp, self.graph = get_interpreter(fn, args, view=False)
@@ -46,6 +46,7 @@
         interp.frame_class = LLSTMFrame
         #
         self.translator = interp.typer.annotator.translator
+        self.translator.config.translation.gcremovetypeptr = gcremovetypeptr
         self.stmtransformer = STMTransformer(self.translator)
         if self.do_jit_driver:
             self.stmtransformer.transform_jit_driver()
@@ -59,8 +60,9 @@
         if self.do_jit_driver:
             import py
             py.test.skip("XXX how to test?")
-        result = interp.eval_graph(self.graph, args)
-        return result
+        if run:
+            result = interp.eval_graph(self.graph, args)
+            return result
 
 
 class LLSTMFrame(LLFrame):
@@ -76,53 +78,90 @@
 
     def check_category(self, p, expected):
         cat = self.get_category_or_null(p)
-        assert cat in 'NPRW'
+        assert cat is None or cat in 'AIQRVW'
+        if expected is not None:
+            assert cat is not None and cat >= expected
         return cat
 
     def op_stm_barrier(self, kind, obj):
         frm, middledigit, to = kind
         assert middledigit == '2'
         cat = self.check_category(obj, frm)
-        if not NEEDS_BARRIER[cat, to]:
+        if not needs_barrier(cat, to):
             # a barrier, but with no effect
             self.llinterpreter.tester.barriers.append(kind.lower())
             return obj
         else:
             # a barrier, calling a helper
             ptr2 = _stmptr(obj, to)
-            if to == 'W':
+            if to >= 'V':
                 self.llinterpreter.tester.writemode.add(ptr2._obj)
             self.llinterpreter.tester.barriers.append(kind)
             return ptr2
 
     def op_stm_ptr_eq(self, obj1, obj2):
-        self.check_category(obj1, 'P')
-        self.check_category(obj2, 'P')
+        self.check_category(obj1, None)
+        self.check_category(obj2, None)
         self.llinterpreter.tester.barriers.append('=')
         return obj1 == obj2
 
     def op_getfield(self, obj, field):
-        if not obj._TYPE.TO._immutable_field(field):
-            self.check_category(obj, 'R')
+        if obj._TYPE.TO._gckind == 'gc':
+            if obj._TYPE.TO._immutable_field(field):
+                expected = 'I'
+            else:
+                expected = 'R'
+            self.check_category(obj, expected)
         return LLFrame.op_getfield(self, obj, field)
 
     def op_setfield(self, obj, fieldname, fieldvalue):
-        if not obj._TYPE.TO._immutable_field(fieldname):
-            self.check_category(obj, 'W')
-            # convert R -> P all other pointers to the same object we can find
+        if obj._TYPE.TO._gckind == 'gc':
+            T = lltype.typeOf(fieldvalue)
+            if isinstance(T, lltype.Ptr) and T.TO._gckind == 'gc':
+                self.check_category(obj, 'W')
+            else:
+                self.check_category(obj, 'V')
+            # convert R -> Q all other pointers to the same object we can find
             for p in self.all_stm_ptrs():
                 if p._category == 'R' and p._T == obj._T and p == obj:
-                    _stmptr._category.__set__(p, 'P')
+                    _stmptr._category.__set__(p, 'Q')
         return LLFrame.op_setfield(self, obj, fieldname, fieldvalue)
 
     def op_cast_pointer(self, RESTYPE, obj):
-        cat = self.check_category(obj, 'P')
-        p = opimpl.op_cast_pointer(RESTYPE, obj)
-        return _stmptr(p, cat)
+        if obj._TYPE.TO._gckind == 'gc':
+            cat = self.check_category(obj, None)
+            p = opimpl.op_cast_pointer(RESTYPE, obj)
+            return _stmptr(p, cat)
+        return lltype.cast_pointer(RESTYPE, obj)
     op_cast_pointer.need_result_type = True
 
+    def op_cast_opaque_ptr(self, RESTYPE, obj):
+        if obj._TYPE.TO._gckind == 'gc':
+            cat = self.check_category(obj, None)
+            p = lltype.cast_opaque_ptr(RESTYPE, obj)
+            return _stmptr(p, cat)
+        return LLFrame.op_cast_opaque_ptr(self, RESTYPE, obj)
+    op_cast_opaque_ptr.need_result_type = True
+
     def op_malloc(self, obj, flags):
+        assert flags['flavor'] == 'gc'
+        # convert all existing pointers W -> V
+        for p in self.all_stm_ptrs():
+            if p._category == 'W':
+                _stmptr._category.__set__(p, 'V')
         p = LLFrame.op_malloc(self, obj, flags)
         ptr2 = _stmptr(p, 'W')
         self.llinterpreter.tester.writemode.add(ptr2._obj)
         return ptr2
+
+    def transaction_break(self):
+        # convert -> I all other pointers to the same object we can find
+        for p in self.all_stm_ptrs():
+            if p._category > 'I':
+                _stmptr._category.__set__(p, 'I')
+
+    def op_stm_commit_transaction(self):
+        self.transaction_break()
+
+    def op_stm_begin_inevitable_transaction(self):
+        self.transaction_break()
diff --git a/rpython/translator/stm/transform.py 
b/rpython/translator/stm/transform.py
--- a/rpython/translator/stm/transform.py
+++ b/rpython/translator/stm/transform.py
@@ -3,6 +3,7 @@
 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
 
@@ -11,6 +12,7 @@
 
     def __init__(self, translator):
         self.translator = translator
+        self.barrier_counts = {}
 
     def transform(self):
         assert not hasattr(self.translator, 'stm_transformation_applied')
@@ -28,10 +30,14 @@
     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:
diff --git a/rpython/translator/stm/writebarrier.py 
b/rpython/translator/stm/writebarrier.py
--- a/rpython/translator/stm/writebarrier.py
+++ b/rpython/translator/stm/writebarrier.py
@@ -1,5 +1,6 @@
 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
 
@@ -9,15 +10,6 @@
     'malloc_nonmovable', 'malloc_nonmovable_varsize',
     ])
 
-NEEDS_BARRIER = {
-    ('P', 'R'): True,
-    ('P', 'W'): True,
-    ('R', 'R'): False,
-    ('R', 'W'): True,
-    ('W', 'R'): False,
-    ('W', 'W'): False,
-    }
-
 def unwraplist(list_v):
     for v in list_v: 
         if isinstance(v, Constant):
@@ -42,153 +34,337 @@
         return OUTER._immutable_interiorfield(unwraplist(op.args[1:-1]))
     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] * len(block.inputargs)
+        self.inputargs_category_per_link = {}
+
+
+    def analyze_inside_block(self):
+        gcremovetypeptr = (
+            self.stmtransformer.translator.config.translation.gcremovetypeptr)
+        wants_a_barrier = {}
+        expand_comparison = set()
+        for op in self.block.operations:
+            is_getter = (op.opname in ('getfield', 'getarrayitem',
+                                       'getinteriorfield') 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')
+                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') 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)
+        #
+        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 None
+            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 cat is not None and is_gc_ptr(v.concretetype):
+                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 None 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 in range(len(self.block.inputargs)):
+            cat = None
+            for output_categories in values:
+                cat2 = output_categories[i]
+                if cat is None:
+                    cat = cat2
+                elif cat2 is not None:
+                    cat = min(cat, cat2)
+            newcats.append(cat)
+        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):
     """This function uses the following characters for 'categories':
 
-           * 'P': a general pointer
+           * '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
+
+       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.
     """
     graphinfo = stmtransformer.write_analyzer.compute_graph_info(graph)
+    annotator = stmtransformer.translator.annotator
+    insert_empty_startblock(annotator, graph)
 
-    def get_category(v):
-        return category.get(v, 'P')
-
-    def get_category_or_null(v):
-        if isinstance(v, Constant) and not v.value:
-            return 'N'
-        return category.get(v, 'P')
-
-    def renamings_get(v):
-        if v not in renamings:
-            return v
-        v2 = renamings[v][0]
-        if v2.concretetype == v.concretetype:
-            return v2
-        v3 = varoftype(v.concretetype)
-        newoperations.append(SpaceOperation('cast_pointer', [v2], v3))
-        return v3
+    block_transformers = {}
+    pending = set()
 
     for block in graph.iterblocks():
         if block.operations == ():
             continue
-        #
-        wants_a_barrier = {}
-        expand_comparison = set()
-        for op in block.operations:
-            # [1] XXX we can't leave getarraysize or the immutable getfields
-            #     fully unmodified.  We'd need at least some lightweight
-            #     read barrier to detect stubs.  For now we just put a
-            #     regular read barrier.
-            if (op.opname in ('getfield', 'getarrayitem',
-                              'getinteriorfield',
-                              'getarraysize', 'getinteriorarraysize', # XXX [1]
-                              ) and
-                  op.result.concretetype is not lltype.Void and
-                  op.args[0].concretetype.TO._gckind == 'gc' and
-                  True): #not is_immutable(op)): XXX see [1]
-                wants_a_barrier[op] = 'R'
-            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_barrier[op] = 'W'
-            elif (op.opname in ('ptr_eq', 'ptr_ne') and
-                  op.args[0].concretetype.TO._gckind == 'gc'):
-                expand_comparison.add(op)
-        #
-        if wants_a_barrier or expand_comparison:
-            # 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: [var-in-newoperations] (len 1)}
-            category = {}    # {var-in-newoperations: LETTER}
-            newoperations = []
-            for op in block.operations:
-                #
-                if op.opname == 'cast_pointer':
-                    v = op.args[0]
-                    renamings[op.result] = renamings.setdefault(v, [v])
-                    continue
-                #
-                to = wants_a_barrier.get(op)
-                if to is not None:
-                    v = op.args[0]
-                    v_holder = renamings.setdefault(v, [v])
-                    v = v_holder[0]
-                    frm = get_category(v)
-                    if NEEDS_BARRIER[frm, to]:
-                        c_info = Constant('%s2%s' % (frm, to), lltype.Void)
-                        w = varoftype(v.concretetype)
-                        newop = SpaceOperation('stm_barrier', [c_info, v], w)
-                        newoperations.append(newop)
-                        v_holder[0] = w
-                        category[w] = to
-                        if to == 'W':
-                            # if any of the other vars in the same path
-                            # points to the same object, they must lose
-                            # their read-status now
-                            for u in block.getvariables():
-                                if get_category(u) == 'R' \
-                                  and u.concretetype == v.concretetype:
-                                    category[u] = 'P'
-                            
-                #
-                newop = SpaceOperation(op.opname,
-                                       [renamings_get(v) for v in op.args],
-                                       op.result)
-                newoperations.append(newop)
-                #
-                if op in expand_comparison:
-                    cats = (get_category_or_null(newop.args[0]),
-                            get_category_or_null(newop.args[1]))
-                    if 'N' not in cats and cats != ('W', 'W'):
-                        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'
+        bt = BlockTransformer(stmtransformer, block)
+        bt.analyze_inside_block()
+        block_transformers[block] = bt
+        pending.add(bt)
 
-                if stmtransformer.collect_analyzer.analyze(op):
-                    # this operation can collect: we bring all 'W'
-                    # categories back to 'R', because we would need
-                    # another stm_write_barrier on them afterwards
-                    for v, cat in category.items():
-                        if cat == 'W':
-                            category[v] = 'R'
+    while pending:
+        # XXX sadly, this seems to be order-dependent.  Picking the minimum
+        # of the blocks seems to be necessary, too, to avoid the situation
+        # of two blocks chasing each other around a loop :-(
+        bt = min(pending)
+        pending.remove(bt)
+        bt.flow_through_block(graphinfo)
+        pending |= bt.update_targets(block_transformers)
 
-                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 'P' because
-                        # we would need another stm_read_barrier()
-                        for v, cat in category.items():
-                            if cat == 'R':
-                                category[v] = 'P'
-                    else:
-                        # the same, but only on objects of the right types
-                        types = set([entry[1] for entry in effectinfo])
-                        for v in category.keys():
-                            if v.concretetype in types and category[v] == 'R':
-                                category[v] = 'P'
-
-                if op.opname in MALLOCS:
-                    category[op.result] = 'W'
-
-            block.operations = newoperations
-            #
-            for link in block.exits:
-                newoperations = []
-                for i, v in enumerate(link.args):
-                    link.args[i] = renamings_get(v)
-                if newoperations:
-                    # must put them in a fresh block along the link
-                    annotator = stmtransformer.translator.annotator
-                    newblock = insert_empty_block(annotator, link,
-                                                  newoperations)
+    for bt in block_transformers.values():
+        bt.patch_now()
_______________________________________________
pypy-commit mailing list
[email protected]
http://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to