[pypy-commit] pypy op_malloc_gc: In-progress.

arigo noreply at buildbot.pypy.org
Fri Nov 18 10:17:31 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: op_malloc_gc
Changeset: r49509:40d91227555d
Date: 2011-11-17 17:30 +0100
http://bitbucket.org/pypy/pypy/changeset/40d91227555d/

Log:	In-progress.

diff --git a/pypy/jit/backend/llsupport/descr.py b/pypy/jit/backend/llsupport/descr.py
--- a/pypy/jit/backend/llsupport/descr.py
+++ b/pypy/jit/backend/llsupport/descr.py
@@ -19,6 +19,7 @@
         self._cache_size = {}
         self._cache_field = {}
         self._cache_array = {}
+        self._cache_arraylen = {}
         self._cache_call = {}
         self._cache_interiorfield = {}
 
@@ -150,6 +151,18 @@
         cachedict[fieldname] = fielddescr
         return fielddescr
 
+def get_field_arraylen_descr(gccache, ARRAY):
+    cache = gccache._cache_arraylen
+    try:
+        return cache[ARRAY]
+    except KeyError:
+        tsc = gccache.translate_support_code
+        (_, _, ofs) = symbolic.get_array_token(ARRAY, tsc)
+        SignedFieldDescr = getFieldDescrClass(lltype.Signed)
+        result = SignedFieldDescr("len", ofs)
+        cache[ARRAY] = result
+        return result
+
 # ____________________________________________________________
 # ArrayDescrs
 
@@ -270,6 +283,8 @@
         else:
             assert isinstance(ARRAY, lltype.GcArray)
             arraydescr = getArrayDescrClass(ARRAY)()
+            arraydescr.field_arraylen_descr = get_field_arraylen_descr(
+                gccache, ARRAY)
         # verify basic assumption that all arrays' basesize and ofslength
         # are equal
         basesize, itemsize, ofslength = symbolic.get_array_token(ARRAY, False)
diff --git a/pypy/jit/backend/llsupport/gc.py b/pypy/jit/backend/llsupport/gc.py
--- a/pypy/jit/backend/llsupport/gc.py
+++ b/pypy/jit/backend/llsupport/gc.py
@@ -17,6 +17,7 @@
 from pypy.jit.backend.llsupport.descr import GcCache, get_field_descr
 from pypy.jit.backend.llsupport.descr import GcPtrFieldDescr
 from pypy.jit.backend.llsupport.descr import get_call_descr
+from pypy.jit.backend.llsupport.descr import get_field_arraylen_descr
 from pypy.rpython.memory.gctransform import asmgcroot
 
 # ____________________________________________________________
@@ -34,8 +35,6 @@
         pass
     def do_write_barrier(self, gcref_struct, gcref_newptr):
         pass
-    def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
-        return operations
     def can_inline_malloc(self, descr):
         return False
     def can_inline_malloc_varsize(self, descr, num_elem):
@@ -61,6 +60,13 @@
                 rgc._make_sure_does_not_move(p)
                 gcrefs_output_list.append(p)
 
+    def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
+        # record all GCREFs, because the GC (or Boehm) cannot see them and
+        # keep them alive if they end up as constants in the assembler
+        for op in operations:
+            self.record_constptrs(op, gcrefs_output_list)
+        return operations
+
 # ____________________________________________________________
 
 class GcLLDescr_boehm(GcLLDescription):
@@ -178,15 +184,6 @@
     def get_funcptr_for_new(self):
         return self.funcptr_for_new
 
-    def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
-        # record all GCREFs too, because Boehm cannot see them and keep them
-        # alive if they end up as constants in the assembler
-        for op in operations:
-            self.record_constptrs(op, gcrefs_output_list)
-        return GcLLDescription.rewrite_assembler(self, cpu, operations,
-                                                 gcrefs_output_list)
-
-
 # ____________________________________________________________
 # All code below is for the hybrid or minimark GC
 
@@ -800,114 +797,10 @@
                     llmemory.cast_ptr_to_adr(gcref_newptr))
 
     def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
-        # Perform two kinds of rewrites in parallel:
-        #
-        # - Add COND_CALLs to the write barrier before SETFIELD_GC and
-        #   SETARRAYITEM_GC operations.
-        #
-        # - Record the ConstPtrs from the assembler.
-        #
-        newops = []
-        known_lengths = {}
-        # we can only remember one malloc since the next malloc can possibly
-        # collect; but we can try to collapse several known-size mallocs into
-        # one, both for performance and to reduce the number of write
-        # barriers.  We do this on each "basic block" of operations, which in
-        # this case means between CALLs or unknown-size mallocs.
-        op_malloc_gc = None
-        v_last_malloc = None
-        previous_size = -1
-        current_mallocs = {}
-        #
-        for op in operations:
-            if op.getopnum() == rop.DEBUG_MERGE_POINT:
-                continue
-            # ---------- record the ConstPtrs ----------
-            self.record_constptrs(op, gcrefs_output_list)
-            # ---------- fold the NEWxxx operations into MALLOC_GC ----------
-            if op.is_malloc():
-                if op.getopnum() == rop.NEW:
-                    descr = op.getdescr()
-                    assert isinstance(descr, BaseSizeDescr)
-                    if op_malloc_gc is None:
-                        # it is the first we see: emit MALLOC_GC
-                        op = ResOperation(rop.MALLOC_GC,
-                                          [ConstInt(descr.size)],
-                                          op.result)
-                        op_malloc_gc = op
-                    else:
-                        # already a MALLOC_GC: increment its total size
-                        total_size = op_malloc_gc.getarg(0).getint()
-                        total_size += descr.size
-                        op_malloc_gc.setarg(0, ConstInt(total_size))
-                        op = ResOperation(rop.INT_ADD,
-                                          [v_last_malloc,
-                                           ConstInt(previous_size)],
-                                          op.result)
-                    previous_size = descr.size
-                    v_last_malloc = op.result
-                    newops.append(op)
-                    # NEW: add a SETFIELD to initialize the GC header
-                    op = ResOperation(rop.SETFIELD_GC,
-                                      [op.result, ConstInt(descr.tid)],
-                                      None, descr=self.fielddescr_tid)
-                    newops.append(op)
-                    continue
-                op_last_malloc = op
-            elif op.can_malloc():
-                op_last_malloc = None
-            # ---------- write barrier for SETFIELD_GC ----------
-            if op.getopnum() == rop.SETFIELD_GC:
-                val = op.getarg(0)
-                # no need for a write barrier in the case of previous malloc
-                if val is not last_malloc:
-                    v = op.getarg(1)
-                    if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and
-                                            bool(v.value)): # store a non-NULL
-                        self._gen_write_barrier(newops, op.getarg(0), v)
-                        op = op.copy_and_change(rop.SETFIELD_RAW)
-            # ---------- write barrier for SETARRAYITEM_GC ----------
-            if op.getopnum() == rop.SETARRAYITEM_GC:
-                val = op.getarg(0)
-                # no need for a write barrier in the case of previous malloc
-                if val is not last_malloc:
-                    v = op.getarg(2)
-                    if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and
-                                            bool(v.value)): # store a non-NULL
-                        self._gen_write_barrier_array(newops, op.getarg(0),
-                                                      op.getarg(1), v,
-                                                      cpu, known_lengths)
-                        op = op.copy_and_change(rop.SETARRAYITEM_RAW)
-            elif op.getopnum() == rop.NEW_ARRAY:
-                v_length = op.getarg(0)
-                if isinstance(v_length, ConstInt):
-                    known_lengths[op.result] = v_length.getint()
-            # ----------
-            newops.append(op)
-        return newops
-
-    def _gen_write_barrier(self, newops, v_base, v_value):
-        args = [v_base, v_value]
-        newops.append(ResOperation(rop.COND_CALL_GC_WB, args, None,
-                                   descr=self.write_barrier_descr))
-
-    def _gen_write_barrier_array(self, newops, v_base, v_index, v_value,
-                                 cpu, known_lengths):
-        if self.write_barrier_descr.get_write_barrier_from_array_fn(cpu) != 0:
-            # If we know statically the length of 'v', and it is not too
-            # big, then produce a regular write_barrier.  If it's unknown or
-            # too big, produce instead a write_barrier_from_array.
-            LARGE = 130
-            length = known_lengths.get(v_base, LARGE)
-            if length >= LARGE:
-                # unknown or too big: produce a write_barrier_from_array
-                args = [v_base, v_index, v_value]
-                newops.append(ResOperation(rop.COND_CALL_GC_WB_ARRAY, args,
-                                           None,
-                                           descr=self.write_barrier_descr))
-                return
-        # fall-back case: produce a write_barrier
-        self._gen_write_barrier(newops, v_base, v_value)
+        rewriter = GcRewriterAssembler(self, cpu)
+        newops = rewriter.rewrite(operations)
+        return GcLLDescription.rewrite_assembler(self, cpu, newops,
+                                                 gcrefs_output_list)
 
     def can_inline_malloc(self, descr):
         assert isinstance(descr, BaseSizeDescr)
@@ -934,6 +827,146 @@
     def freeing_block(self, start, stop):
         self.gcrootmap.freeing_block(start, stop)
 
+
+class GcRewriterAssembler(object):
+    # This class performs the following rewrites on the list of operations:
+    #
+    # - Remove the DEBUG_MERGE_POINTs.
+    #
+    # - Turn all NEW_xxx to MALLOC_GC operations, possibly followed by
+    #   SETFIELDs in order set their GC fields.
+    #
+    # - Add COND_CALLs to the write barrier before SETFIELD_GC and
+    #   SETARRAYITEM_GC operations.
+
+    def __init__(self, gc_ll_descr, cpu):
+        self.gc_ll_descr = gc_ll_descr
+        self.cpu = cpu
+        self.tsc = self.gc_ll_descr.translate_support_code
+
+    def rewrite(self, operations):
+        self.newops = []
+        self.known_lengths = {}
+        # we can only remember one malloc since the next malloc can possibly
+        # collect; but we can try to collapse several known-size mallocs into
+        # one, both for performance and to reduce the number of write
+        # barriers.  We do this on each "basic block" of operations, which in
+        # this case means between CALLs or unknown-size mallocs.
+        self.op_malloc_gc = None
+        self.v_last_malloc = None
+        self.previous_size = -1
+        #
+        for op in operations:
+            if op.getopnum() == rop.DEBUG_MERGE_POINT:
+                continue
+            # ---------- fold the NEWxxx operations into MALLOC_GC ----------
+            if op.is_malloc():
+                if op.getopnum() == rop.NEW:
+                    descr = op.getdescr()
+                    assert isinstance(descr, BaseSizeDescr)
+                    self.gen_malloc_const(descr.size, op.result)
+                    self.gen_initialize_tid(op.result, descr.tid)
+                    continue
+                if op.getopnum() == rop.NEW_ARRAY:
+                    v_newlength = op.getarg(0)
+                    if isinstance(v_newlength, ConstInt):
+                        newlength = v_newlength.getint()
+                        self.known_lengths[op.result] = newlength
+                        descr = op.getdescr()
+                        assert isinstance(descr, BaseArrayDescr)
+                        basesize = descr.get_base_size(self.tsc)
+                        itemsize = descr.get_item_size(self.tsc)
+                        fullsize = basesize + newlength * itemsize
+                        self.gen_malloc_const(fullsize, op.result)
+                        self.gen_initialize_tid(op.result, descr.tid)
+                        self.gen_initialize_len(op.result, v_newlength, descr)
+                        continue
+                    yyyyy
+                xxxx
+            elif op.can_malloc():
+                self.op_malloc_gc = None
+            # ---------- write barrier for SETFIELD_GC ----------
+            if op.getopnum() == rop.SETFIELD_GC:
+                val = op.getarg(0)
+                # no need for a write barrier in the case of previous malloc
+                if val is not last_malloc:
+                    v = op.getarg(1)
+                    if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and
+                                            bool(v.value)): # store a non-NULL
+                        self.gen_write_barrier(op.getarg(0), v)
+                        op = op.copy_and_change(rop.SETFIELD_RAW)
+            # ---------- write barrier for SETARRAYITEM_GC ----------
+            if op.getopnum() == rop.SETARRAYITEM_GC:
+                val = op.getarg(0)
+                # no need for a write barrier in the case of previous malloc
+                if val is not last_malloc:
+                    v = op.getarg(2)
+                    if isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and
+                                            bool(v.value)): # store a non-NULL
+                        self.gen_write_barrier_array(op.getarg(0),
+                                                     op.getarg(1), v)
+                        op = op.copy_and_change(rop.SETARRAYITEM_RAW)
+            # ----------
+            self.newops.append(op)
+        return self.newops
+
+    def gen_malloc_const(self, size, v_result):
+        if self.op_malloc_gc is None:
+            # it is the first we see: emit MALLOC_GC
+            op = ResOperation(rop.MALLOC_GC,
+                              [ConstInt(size)],
+                              v_result)
+            self.op_malloc_gc = op
+        else:
+            # already a MALLOC_GC: increment its total size
+            total_size = self.op_malloc_gc.getarg(0).getint()
+            total_size += size
+            self.op_malloc_gc.setarg(0, ConstInt(total_size))
+            op = ResOperation(rop.INT_ADD,
+                              [self.v_last_malloc,
+                               ConstInt(self.previous_size)],
+                              v_result)
+        self.previous_size = size
+        self.v_last_malloc = v_result
+        self.newops.append(op)
+
+    def gen_initialize_tid(self, v_newgcobj, tid):
+        # produce a SETFIELD to initialize the GC header
+        op = ResOperation(rop.SETFIELD_GC,
+                          [v_newgcobj, ConstInt(tid)], None,
+                          descr=self.gc_ll_descr.fielddescr_tid)
+        self.newops.append(op)
+
+    def gen_initialize_len(self, v_newgcobj, v_length, arraydescr):
+        # produce a SETFIELD to initialize the array length
+        op = ResOperation(rop.SETFIELD_GC,
+                          [v_newgcobj, v_length], None,
+                          descr=arraydescr.field_arraylen_descr)
+        self.newops.append(op)
+
+    def gen_write_barrier(self, v_base, v_value):
+        args = [v_base, v_value]
+        self.newops.append(ResOperation(rop.COND_CALL_GC_WB, args, None,
+                                        descr=self.write_barrier_descr))
+
+    def gen_write_barrier_array(self, v_base, v_index, v_value):
+        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
+        if write_barrier_descr.get_write_barrier_from_array_fn(self.cpu) != 0:
+            # If we know statically the length of 'v', and it is not too
+            # big, then produce a regular write_barrier.  If it's unknown or
+            # too big, produce instead a write_barrier_from_array.
+            LARGE = 130
+            length = self.known_lengths.get(v_base, LARGE)
+            if length >= LARGE:
+                # unknown or too big: produce a write_barrier_from_array
+                args = [v_base, v_index, v_value]
+                self.newops.append(
+                    ResOperation(rop.COND_CALL_GC_WB_ARRAY, args, None,
+                                 descr=write_barrier_descr))
+                return
+        # fall-back case: produce a write_barrier
+        self.gen_write_barrier(v_base, v_value)
+
 # ____________________________________________________________
 
 def get_ll_description(gcdescr, translator=None, rtyper=None):
diff --git a/pypy/jit/backend/llsupport/test/test_descr.py b/pypy/jit/backend/llsupport/test/test_descr.py
--- a/pypy/jit/backend/llsupport/test/test_descr.py
+++ b/pypy/jit/backend/llsupport/test/test_descr.py
@@ -1,4 +1,4 @@
-from pypy.rpython.lltypesystem import lltype, rffi
+from pypy.rpython.lltypesystem import lltype, rffi, rstr
 from pypy.jit.backend.llsupport.descr import *
 from pypy.jit.backend.llsupport import symbolic
 from pypy.rlib.objectmodel import Symbolic
@@ -448,3 +448,19 @@
     res = descr2.call_stub(rffi.cast(lltype.Signed, fnptr),
                            [a, b, c], [], [])
     assert float(uint2singlefloat(rffi.r_uint(res))) == -11.5
+
+def test_field_arraylen_descr():
+    c0 = GcCache(True)
+    A1 = lltype.GcArray(lltype.Signed)
+    fielddescr = get_field_arraylen_descr(c0, A1)
+    assert isinstance(fielddescr, BaseFieldDescr)
+    ofs = fielddescr.offset
+    assert repr(ofs) == '< ArrayLengthOffset <GcArray of Signed > >'
+    #
+    fielddescr = get_field_arraylen_descr(c0, rstr.STR)
+    ofs = fielddescr.offset
+    assert repr(ofs) == ("< <FieldOffset <GcStruct rpy_string { hash, chars }>"
+                         " 'chars'> + < ArrayLengthOffset"
+                         " <Array of Char > > >")
+    # caching:
+    assert fielddescr is get_field_arraylen_descr(c0, rstr.STR)
diff --git a/pypy/jit/backend/llsupport/test/test_gc.py b/pypy/jit/backend/llsupport/test/test_gc.py
--- a/pypy/jit/backend/llsupport/test/test_gc.py
+++ b/pypy/jit/backend/llsupport/test/test_gc.py
@@ -632,6 +632,34 @@
                                                         operations, [])
         equaloplists(operations, expected.operations)
 
+    def test_rewrite_assembler_new_array_fixed_to_malloc(self):
+        self.gc_ll_descr.translate_support_code = False
+        try:
+            A = lltype.GcArray(lltype.Signed)
+            adescr = get_array_descr(self.gc_ll_descr, A)
+            adescr.tid = 1234
+            lengthdescr = get_field_arraylen_descr(self.gc_ll_descr, A)
+        finally:
+            self.gc_ll_descr.translate_support_code = True
+        tiddescr = self.gc_ll_descr.fielddescr_tid
+        ops = parse("""
+        []
+        p0 = new_array(10, descr=adescr)
+        jump()
+        """, namespace=locals())
+        expected = parse("""
+        []
+        p0 = malloc_gc(%d)
+        setfield_gc(p0, 1234, descr=tiddescr)
+        setfield_gc(p0, 10, descr=lengthdescr)
+        jump()
+        """ % (adescr.get_base_size(False) + 10 * adescr.get_item_size(False),),
+                         namespace=locals())
+        operations = get_deep_immutable_oplist(ops.operations)
+        operations = self.gc_ll_descr.rewrite_assembler(self.fake_cpu,
+                                                        operations, [])
+        equaloplists(operations, expected.operations)
+
     def test_rewrite_assembler_initialization_store(self):
         S = lltype.GcStruct('S', ('parent', OBJECT),
                             ('x', lltype.Signed))


More information about the pypy-commit mailing list