[pypy-svn] r47559 - in pypy/dist/pypy/rpython/memory: gc gctransform gctransform/test

arigo at codespeak.net arigo at codespeak.net
Thu Oct 18 19:40:50 CEST 2007


Author: arigo
Date: Thu Oct 18 19:40:49 2007
New Revision: 47559

Modified:
   pypy/dist/pypy/rpython/memory/gc/base.py
   pypy/dist/pypy/rpython/memory/gc/generation.py
   pypy/dist/pypy/rpython/memory/gctransform/framework.py
   pypy/dist/pypy/rpython/memory/gctransform/refcounting.py
   pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py
   pypy/dist/pypy/rpython/memory/gctransform/transform.py
Log:
(arigato, cfbolz before he ran for his train):
* gctransformer support for write_barrier
* fix and expand the write_barrier_support test
* clean up the interface of push_alive() and pop_alive()
* fix tests that depends on choose_gc_from_config() picking
  a default GC if none is specified.


Modified: pypy/dist/pypy/rpython/memory/gc/base.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc/base.py	(original)
+++ pypy/dist/pypy/rpython/memory/gc/base.py	Thu Oct 18 19:40:49 2007
@@ -4,6 +4,7 @@
 class GCBase(object):
     _alloc_flavor_ = "raw"
     moving_gc = False
+    needs_write_barrier = False
 
     def set_query_functions(self, is_varsize, getfinalizer,
                             offsets_to_gc_pointers,
@@ -175,7 +176,8 @@
 def choose_gc_from_config(config):
     """Return a (GCClass, GC_PARAMS) from the given config object.
     """
-    assert config.translation.gctransformer == "framework"
+    if config.translation.gctransformer != "framework":   # for tests
+        config.translation.gc = "marksweep"     # crash if inconsistent
     if config.translation.gc == "marksweep":
         GC_PARAMS = {'start_heap_size': 8*1024*1024} # XXX adjust
         from pypy.rpython.memory.gc.marksweep import MarkSweepGC
@@ -194,5 +196,5 @@
         from pypy.rpython.memory.gc.generation import GenerationGC
         return GenerationGC, GC_PARAMS
     else:
-        raise ValueError("unknown value for frameworkgc: %r" % (
-            config.translation.frameworkgc,))
+        raise ValueError("unknown value for translation.gc: %r" % (
+            config.translation.gc,))

Modified: pypy/dist/pypy/rpython/memory/gc/generation.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc/generation.py	(original)
+++ pypy/dist/pypy/rpython/memory/gc/generation.py	Thu Oct 18 19:40:49 2007
@@ -15,6 +15,7 @@
     list, chained to each other via their 'forw' header field.
     """
     inline_simple_malloc = True
+    needs_write_barrier = True
 
     def __init__(self, AddressLinkedList,
                  nursery_size=128,

Modified: pypy/dist/pypy/rpython/memory/gctransform/framework.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/framework.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform/framework.py	Thu Oct 18 19:40:49 2007
@@ -9,6 +9,7 @@
 from pypy.rlib.rarithmetic import ovfcheck
 from pypy.rlib.objectmodel import debug_assert
 from pypy.translator.backendopt import graphanalyze
+from pypy.translator.backendopt.support import var_needsgc
 from pypy.annotation import model as annmodel
 from pypy.rpython import annlowlevel
 from pypy.rpython.rbuiltin import gen_cast
@@ -248,6 +249,15 @@
         else:
             self.id_ptr = None
 
+        if GCClass.needs_write_barrier:
+            self.write_barrier_ptr = getfn(GCClass.write_barrier.im_func,
+                                           [s_gc, annmodel.SomeAddress(),
+                                            annmodel.SomeAddress(),
+                                            annmodel.SomeAddress()],
+                                           annmodel.s_None,
+                                           inline=True)
+        else:
+            self.write_barrier_ptr = None
         self.statistics_ptr = getfn(GCClass.statistics.im_func,
                                     [s_gc, annmodel.SomeInteger()],
                                     annmodel.SomeInteger())
@@ -561,6 +571,60 @@
         else:
             hop.rename('cast_ptr_to_int')     # works nicely for non-moving GCs
 
+    def transform_generic_set(self, hop):
+        if self.write_barrier_ptr is None:
+            super(FrameworkGCTransformer, self).transform_generic_set(hop)
+        else:
+            v_struct = hop.spaceop.args[0]
+            v_newvalue = hop.spaceop.args[-1]
+            assert isinstance(v_newvalue.concretetype, lltype.Ptr)
+            opname = hop.spaceop.opname
+            assert opname in ('setfield', 'setarrayitem', 'setinteriorfield')
+            offsets = hop.spaceop.args[1:-1]
+            CURTYPE = v_struct.concretetype.TO
+            v_currentofs = None
+            for ofs in offsets:
+                if ofs.concretetype is lltype.Void:
+                    # a field in a structure
+                    fieldname = ofs.value
+                    fieldofs = llmemory.offsetof(CURTYPE, fieldname)
+                    v_offset = rmodel.inputconst(lltype.Signed, fieldofs)
+                    CURTYPE = getattr(CURTYPE, fieldname)
+                else:
+                    # an index in an array
+                    assert ofs.concretetype is lltype.Signed
+                    firstitem = llmemory.ArrayItemsOffset(CURTYPE)
+                    itemsize  = llmemory.sizeof(CURTYPE.OF)
+                    c_firstitem = rmodel.inputconst(lltype.Signed, firstitem)
+                    c_itemsize  = rmodel.inputconst(lltype.Signed, itemsize)
+                    v_index = hop.spaceop.args[1]
+                    v_offset = hop.genop("int_mul", [c_itemsize, v_index],
+                                         resulttype = lltype.Signed)
+                    v_offset = hop.genop("int_add", [c_firstitem, v_offset],
+                                         resulttype = lltype.Signed)
+                    CURTYPE = CURTYPE.OF
+                if v_currentofs is None:
+                    v_currentofs = v_offset
+                else:
+                    v_currentofs = hop.genop("int_add",
+                                             [v_currentofs, v_offset],
+                                             resulttype = lltype.Signed)
+            # XXX for some GCs we could skip the write_barrier if v_newvalue
+            # is a constant
+            v_newvalue = hop.genop("cast_ptr_to_adr", [v_newvalue],
+                                   resulttype = llmemory.Address)
+            v_structaddr = hop.genop("cast_ptr_to_adr", [v_struct],
+                                     resulttype = llmemory.Address)
+            v_fieldaddr = hop.genop("adr_add", [v_structaddr, v_currentofs],
+                                    resulttype = llmemory.Address)
+            hop.genop("direct_call", [self.write_barrier_ptr,
+                                      v_newvalue,
+                                      v_fieldaddr,
+                                      v_structaddr])
+
+    def var_needs_set_transform(self, var):
+        return var_needsgc(var)
+
     def push_alive_nopyobj(self, var, llops):
         pass
 
@@ -573,10 +637,10 @@
         if self.gcdata.gc.moving_gc:
             # moving GCs don't borrow, so the caller does not need to keep
             # the arguments alive
-            livevars = [var for var in self.livevars_after_op
+            livevars = [var for var in hop.livevars_after_op()
                             if not var_ispyobj(var)]
         else:
-            livevars = self.livevars_after_op + self.current_op_keeps_alive
+            livevars = hop.livevars_after_op() + hop.current_op_keeps_alive()
             livevars = [var for var in livevars if not var_ispyobj(var)]
         self.num_pushs += len(livevars)
         if not livevars:

Modified: pypy/dist/pypy/rpython/memory/gctransform/refcounting.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/refcounting.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform/refcounting.py	Thu Oct 18 19:40:49 2007
@@ -154,11 +154,11 @@
 
     def gct_gc_protect(self, hop):
         """ protect this object from gc (make it immortal) """
-        self.push_alive(hop.spaceop.args[0])
+        self.push_alive(hop.spaceop.args[0], hop.llops)
 
     def gct_gc_unprotect(self, hop):
         """ get this object back into gc control """
-        self.pop_alive(hop.spaceop.args[0])
+        self.pop_alive(hop.spaceop.args[0], hop.llops)
 
     def gct_fv_gc_malloc(self, hop, flags, TYPE, c_size):
         v_raw = hop.genop("direct_call", [self.malloc_fixedsize_ptr, c_size],

Modified: pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform/test/test_framework.py	Thu Oct 18 19:40:49 2007
@@ -1,6 +1,6 @@
 from pypy.objspace.flow.model import Constant, SpaceOperation
 from pypy.annotation.model import SomeInteger
-from pypy.rpython.memory.gc.base import GCBase
+from pypy.rpython.memory.gc.marksweep import MarkSweepGC
 from pypy.rpython.memory.gctransform.test.test_transform import rtype
 from pypy.rpython.memory.gctransform.transform import GcHighLevelOp
 from pypy.rpython.memory.gctransform.framework import FrameworkGCTransformer, CollectAnalyzer
@@ -63,27 +63,49 @@
 
 class WriteBarrierTransformer(FrameworkGCTransformer):
     GC_PARAMS = {}
-    class GC_CLASS(GCBase):
-        def write_barrier(self, addr, addr_to, addr_struct):
-            addr_to.address[0] = addr
+    class GCClass(MarkSweepGC):
+        needs_write_barrier = True
 
-def test_write_barrier_support():
-    py.test.skip("no write barrier support yet!")
+def write_barrier_check(spaceop):
     t = TranslationContext()
     t.buildannotator().build_types(lambda x:x, [SomeInteger()])
     t.buildrtyper().specialize()
-    llops = LowLevelOpList()
-    PTR_TYPE = lltype.Ptr(lltype.GcStruct('S', ('x', lltype.Signed)))
-    spaceop = SpaceOperation(
-        "setfield",
-        [varoftype(PTR_TYPE), Constant('x', lltype.Void)],
-        varoftype(lltype.Void))
     transformer = WriteBarrierTransformer(t)
+    llops = LowLevelOpList()
     hop = GcHighLevelOp(transformer, spaceop, 0, llops)
     hop.dispatch()
     found = False
+    print spaceop, '======>'
     for op in llops:
+        print '\t', op
         if op.opname == 'direct_call':
             found = True
-            break
     assert found
+
+def test_write_barrier_support_setfield():
+    PTR_TYPE2 = lltype.Ptr(lltype.GcStruct('T', ('y', lltype.Signed)))
+    PTR_TYPE = lltype.Ptr(lltype.GcStruct('S', ('x', PTR_TYPE2)))
+    write_barrier_check(SpaceOperation(
+        "setfield",
+        [varoftype(PTR_TYPE), Constant('x', lltype.Void),
+         varoftype(PTR_TYPE2)],
+        varoftype(lltype.Void)))
+
+def test_write_barrier_support_setarrayitem():
+    PTR_TYPE2 = lltype.Ptr(lltype.GcStruct('T', ('y', lltype.Signed)))
+    ARRAYPTR = lltype.Ptr(lltype.GcArray(PTR_TYPE2))
+    write_barrier_check(SpaceOperation(
+        "setarrayitem",
+        [varoftype(ARRAYPTR), varoftype(lltype.Signed),
+         varoftype(PTR_TYPE2)],
+        varoftype(lltype.Void)))
+
+def test_write_barrier_support_setinteriorfield():
+    PTR_TYPE2 = lltype.Ptr(lltype.GcStruct('T', ('y', lltype.Signed)))
+    ARRAYPTR2 = lltype.Ptr(lltype.GcArray(('a', lltype.Signed),
+                                          ('b', PTR_TYPE2)))
+    write_barrier_check(SpaceOperation(
+        "setinteriorfield",
+        [varoftype(ARRAYPTR2), varoftype(lltype.Signed),
+         Constant('b', lltype.Void), varoftype(PTR_TYPE2)],
+        varoftype(lltype.Void)))

Modified: pypy/dist/pypy/rpython/memory/gctransform/transform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform/transform.py	(original)
+++ pypy/dist/pypy/rpython/memory/gctransform/transform.py	Thu Oct 18 19:40:49 2007
@@ -38,10 +38,16 @@
         self.spaceop = op
         self.index = index
         self.llops = llops
-        gct.livevars_after_op = [
+
+    def livevars_after_op(self):
+        gct = self.gctransformer
+        return [
             var for var in gct.livevars
                 if gct.var_last_needed_in[var] > self.index]
-        gct.current_op_keeps_alive = [
+
+    def current_op_keeps_alive(self):
+        gct = self.gctransformer
+        return [
             var for var in self.spaceop.args
                 if gct.var_last_needed_in.get(var) == self.index]
 
@@ -60,9 +66,9 @@
                                  'cast_pointer', 'getsubstruct',
                                  'getinteriorfield'):
                     # XXX more operations?
-                    gct.push_alive(v_result)
+                    gct.push_alive(v_result, self.llops)
             elif opname not in ('direct_call', 'indirect_call'):
-                gct.push_alive(v_result)
+                gct.push_alive(v_result, self.llops)
         
 
 
@@ -157,7 +163,7 @@
         return is_borrowed
 
     def transform_block(self, block, is_borrowed):
-        self.llops = LowLevelOpList()
+        llops = LowLevelOpList()
         #self.curr_block = block
         self.livevars = [var for var in block.inputargs
                     if var_needsgc(var) and not is_borrowed(var)]
@@ -175,7 +181,7 @@
                 self.var_last_needed_in[var] = len(block.operations) + 1
         
         for i, op in enumerate(block.operations):
-            hop = GcHighLevelOp(self, op, i, self.llops)
+            hop = GcHighLevelOp(self, op, i, llops)
             hop.dispatch()
 
         if len(block.exits) != 0: # i.e not the return block
@@ -186,7 +192,7 @@
                 deadinallexits.difference_update(sets.Set(link.args))
 
             for var in deadinallexits:
-                self.pop_alive(var)
+                self.pop_alive(var, llops)
 
             for link in block.exits:
                 livecounts = dict.fromkeys(sets.Set(self.livevars) - deadinallexits, 1)
@@ -201,8 +207,7 @@
                         livecounts[v] = -1
                 self.links_to_split[link] = livecounts
 
-            block.operations[:] = self.llops
-        self.llops = None
+            block.operations[:] = llops
         self.livevars = None
         self.var_last_needed_in = None
 
@@ -294,22 +299,18 @@
         v_old = hop.genop('g' + opname[1:],
                           hop.inputargs()[:-1],
                           resulttype=v_new.concretetype)
-        self.push_alive(v_new)
+        self.push_alive(v_new, hop.llops)
         hop.rename('bare_' + opname)
-        self.pop_alive(v_old)
+        self.pop_alive(v_old, hop.llops)
 
 
-    def push_alive(self, var, llops=None):
-        if llops is None:
-            llops = self.llops
+    def push_alive(self, var, llops):
         if var_ispyobj(var):
             self.push_alive_pyobj(var, llops)
         else:
             self.push_alive_nopyobj(var, llops)
 
-    def pop_alive(self, var, llops=None):
-        if llops is None:
-            llops = self.llops
+    def pop_alive(self, var, llops):
         if var_ispyobj(var):
             self.pop_alive_pyobj(var, llops)
         else:
@@ -358,10 +359,10 @@
         BaseGCTransformer.__init__(self, parenttransformer.translator)
         self.parenttransformer = parenttransformer
 
-    def push_alive(self, var, llops=None):
+    def push_alive(self, var, llops):
         pass
 
-    def pop_alive(self, var, llops=None):
+    def pop_alive(self, var, llops):
         pass
 
     def gct_malloc(self, hop):



More information about the Pypy-commit mailing list