Commits

Armin Rigo committed a692bba

Add 'with stm_ignored:' to not track in stm the reads and writes
to GC objects

Comments (0)

Files changed (8)

rpython/rlib/objectmodel.py

             raise Exception("import_from_mixin: would overwrite the value "
                             "already defined locally for %r" % (key,))
         target[key] = value
+
+# ____________________________________________________________
+
+class _StmIgnored:
+    def __enter__(self):
+        "NOT_RPYTHON"
+    def __exit__(self, *args):
+        "NOT_RPYTHON"
+
+class Entry(ExtRegistryEntry):
+    _about_ = _StmIgnored.__enter__.im_func
+    def compute_result_annotation(self, *args_s):
+        return None
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        hop.genop('stm_ignored_start', [])
+
+class Entry(ExtRegistryEntry):
+    _about_ = _StmIgnored.__exit__.im_func
+    def compute_result_annotation(self, *args_s):
+        return None
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        hop.genop('stm_ignored_stop', [])
+
+# Use "with stm_ignored:" around simple field read/write operations
+# that should not be tracked by the STM machinery.  They are always
+# simply performed instead.  It is useful for read/writes that don't
+# need to give a really consistent operation, when an approximative
+# behavior is fine, like incrementing some global counter.
+# XXX only for GC objects for now
+# XXX but it should replace 'stm_dont_track_raw_accesses' too
+# XXX DON'T USE for *writes* of a GC pointer into an object
+stm_ignored = _StmIgnored()

rpython/rtyper/lltypesystem/lloperation.py

     'stm_get_adr_of_private_rev_num':LLOp(),
     'stm_get_adr_of_read_barrier_cache':LLOp(),
 
+    'stm_ignored_start':      LLOp(canrun=True),
+    'stm_ignored_stop':       LLOp(canrun=True),
+
     # __________ address operations __________
 
     'boehm_malloc':         LLOp(),

rpython/rtyper/lltypesystem/opimpl.py

 def op_debug_stm_flush_barrier():
     pass
 
+def op_stm_ignored_start():
+    pass
+
+def op_stm_ignored_stop():
+    pass
+
 def op_stm_ptr_eq(x, y):
     return op_ptr_eq(x, y)
 

rpython/translator/c/funcgen.py

     OP_STM_CLEAR_EXCEPTION_DATA_ON_ABORT= _OP_STM
     OP_STM_ALLOCATE_NONMOVABLE_INT_ADR  = _OP_STM
 
+    def OP_STM_IGNORED_START(self, op):
+        return '/* stm_ignored_start */'
+
+    def OP_STM_IGNORED_STOP(self, op):
+        return '/* stm_ignored_stop */'
+
 
     def OP_PTR_NONZERO(self, op):
         return '%s = (%s != NULL);' % (self.expr(op.result),

rpython/translator/c/test/test_standalone.py

         self.compile(entry_point)
         # assert did not explode
 
+    def test_ignore_stm_ignored(self):
+        from rpython.rlib.objectmodel import stm_ignored
+        def entry_point(argv):
+            with stm_ignored:
+                return len(argv)
+
+        self.compile(entry_point)
+        # assert did not explode
+
+
 class TestMaemo(TestStandalone):
     def setup_class(cls):
         py.test.skip("TestMaemo: tests skipped for now")

rpython/translator/stm/test/test_writebarrier.py

         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']
+
 
 external_release_gil = rffi.llexternal('external_release_gil', [], lltype.Void,
                                        _callable=lambda: None,

rpython/translator/stm/test/transform_support.py

 
 
 class LLSTMFrame(LLFrame):
+    stm_ignored = False
 
     def all_stm_ptrs(self):
         for frame in self.llinterpreter.frame_stack:
         cat = self.get_category_or_null(p)
         assert cat is None or cat in 'AIQRVW'
         if expected is not None:
+            if self.stm_ignored:
+                if expected >= 'W':
+                    raise AssertionError("should not be seen in 'stm_ignored'")
+                if expected > 'I':
+                    expected = 'I'
             assert cat is not None and cat >= expected
         return cat
 
             self.llinterpreter.tester.barriers.append(kind)
             return ptr2
 
+    def op_stm_ignored_start(self):
+        assert self.stm_ignored == False
+        self.stm_ignored = True
+
+    def op_stm_ignored_stop(self):
+        assert self.stm_ignored == True
+        self.stm_ignored = False
+
     def op_stm_ptr_eq(self, obj1, obj2):
         self.check_category(obj1, None)
         self.check_category(obj2, None)

rpython/translator/stm/writebarrier.py

 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([
         self.update_inputargs_category()
 
 
-    def analyze_inside_block(self):
+    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
 
             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))
+                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
 
        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)
         if block.operations == ():
             continue
         bt = BlockTransformer(stmtransformer, block)
-        bt.analyze_inside_block()
+        bt.analyze_inside_block(graph)
         block_transformers[block] = bt
 
     bt = block_transformers[graph.startblock]