[pypy-commit] pypy op_malloc_gc: Refactor this logic into its own class, shared by the two GC strategies.

arigo noreply at buildbot.pypy.org
Sat Nov 26 13:16:05 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: op_malloc_gc
Changeset: r49815:f49a7ba4e56b
Date: 2011-11-26 13:15 +0100
http://bitbucket.org/pypy/pypy/changeset/f49a7ba4e56b/

Log:	Refactor this logic into its own class, shared by the two GC
	strategies.

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
@@ -19,6 +19,7 @@
 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.jit.backend.llsupport.rewrite import GcRewriterAssembler
 from pypy.rpython.memory.gctransform import asmgcroot
 
 # ____________________________________________________________
@@ -57,8 +58,7 @@
     def get_funcptr_for_newunicode(self):
         return llhelper(self.GC_MALLOC_STR_UNICODE, self.malloc_unicode)
 
-
-    def record_constptrs(self, op, gcrefs_output_list):
+    def _record_constptrs(self, op, gcrefs_output_list):
         for i in range(op.numargs()):
             v = op.getarg(i)
             if isinstance(v, ConstPtr) and bool(v.value):
@@ -67,17 +67,20 @@
                 gcrefs_output_list.append(p)
 
     def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
+        rewriter = GcRewriterAssembler(self, cpu)
+        newops = rewriter.rewrite(operations)
         # 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
+        for op in newops:
+            self._record_constptrs(op, gcrefs_output_list)
+        return newops
 
 # ____________________________________________________________
 
 class GcLLDescr_boehm(GcLLDescription):
     moving_gc = False
     gcrootmap = None
+    write_barrier_descr = None
 
     @classmethod
     def configure_boehm_once(cls):
@@ -190,50 +193,6 @@
     def get_funcptr_for_new(self):
         return self.funcptr_for_new
 
-    def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
-        newops = []
-        c_zero = ConstInt(0)
-        tsc = self.translate_support_code
-        for op in operations:
-            if op.is_malloc():
-                # turn all NEW_xxx into a MALLOC_GC
-                opnum = op.getopnum()
-                if opnum == rop.NEW:
-                    descr = op.getdescr()
-                    assert isinstance(descr, BaseSizeDescr)
-                    c_size = ConstInt(descr.size)
-                    op = ResOperation(rop.MALLOC_GC, [c_size, c_zero, c_zero],
-                                      op.result)
-                elif opnum == rop.NEW_WITH_VTABLE:
-                    classint = op.getarg(0).getint()
-                    descr = heaptracker.vtable2descr(cpu, classint)
-                    c_size = ConstInt(descr.size)
-                    op = ResOperation(rop.MALLOC_GC, [c_size, c_zero, c_zero],
-                                      op.result)
-                    if self.fielddescr_vtable is not None:
-                        newops.append(op)
-                        op = ResOperation(rop.SETFIELD_GC,
-                                          [op.result, ConstInt(classint)],
-                                          None, descr=self.fielddescr_vtable)
-                elif opnum == rop.NEW_ARRAY:
-                    descr = op.getdescr()
-                    assert isinstance(descr, BaseArrayDescr)
-                    c_basesize = ConstInt(descr.get_base_size(tsc))
-                    c_itemsize = ConstInt(descr.get_item_size(tsc))
-                    v_length = op.getarg(0)
-                    op = ResOperation(rop.MALLOC_GC, [c_basesize,
-                                                      v_length,
-                                                      c_itemsize],
-                                      op.result)
-                    newops.append(op)
-                    op = ResOperation(rop.SETFIELD_GC, [op.result, v_length],
-                                      None, descr=descr.field_arraylen_descr)
-                else:
-                    raise NotImplementedError(op.getopname())
-            newops.append(op)
-        return GcLLDescription.rewrite_assembler(self, cpu, newops,
-                                                 gcrefs_output_list)
-
 # ____________________________________________________________
 # All code below is for the hybrid or minimark GC
 
@@ -846,12 +805,6 @@
             funcptr(llmemory.cast_ptr_to_adr(gcref_struct),
                     llmemory.cast_ptr_to_adr(gcref_newptr))
 
-    def rewrite_assembler(self, cpu, operations, gcrefs_output_list):
-        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)
         if descr.size < self.max_size_of_young_obj:
@@ -877,162 +830,6 @@
     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.
-
-    _v_last_malloc = None
-    _previous_size = -1
-
-    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
-        self.newops = []
-        self.known_lengths = {}
-        self.op_malloc_gc = None
-        self.current_mallocs = {}     # set of variables
-
-    def rewrite(self, operations):
-        # 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.
-        #
-        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
-                self.current_mallocs.clear()
-            # ---------- 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 not in self.current_mallocs:
-                    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 not in self.current_mallocs:
-                    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):
-        size = self.round_up_for_allocation(size)
-        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.current_mallocs[v_result] = None
-        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):
-        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
-        args = [v_base, v_value]
-        self.newops.append(ResOperation(rop.COND_CALL_GC_WB, args, None,
-                                        descr=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 round_up_for_allocation(self, size):
-        if self.tsc:
-            return llarena.round_up_for_allocation(
-                size, self.gc_ll_descr.minimal_size_in_nursery)
-        else:
-            # non-translated: do it manually
-            # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs
-            size = max(size, 2 * WORD)
-            return (size + WORD-1) & ~(WORD-1)     # round up
-
 # ____________________________________________________________
 
 def get_ll_description(gcdescr, translator=None, rtyper=None):
diff --git a/pypy/jit/backend/llsupport/rewrite.py b/pypy/jit/backend/llsupport/rewrite.py
new file mode 100644
--- /dev/null
+++ b/pypy/jit/backend/llsupport/rewrite.py
@@ -0,0 +1,214 @@
+from pypy.jit.metainterp.history import ConstInt
+from pypy.jit.metainterp.resoperation import ResOperation, rop
+from pypy.jit.codewriter import heaptracker
+from pypy.jit.backend.llsupport.descr import BaseSizeDescr, BaseArrayDescr
+
+
+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 to initialize their GC fields.
+    #
+    # - Add COND_CALLs to the write barrier before SETFIELD_GC and
+    #   SETARRAYITEM_GC operations.
+
+    _v_last_malloc = None
+    _previous_size = -1
+    c_zero = ConstInt(0)
+
+    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
+        self.newops = []
+        self.known_lengths = {}
+        self.op_last_malloc_gc = None
+        self.current_mallocs = {}     # set of variables
+
+    def rewrite(self, operations):
+        # 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.
+        # (XXX later: or LABELs)
+        #
+        for op in operations:
+            if op.getopnum() == rop.DEBUG_MERGE_POINT:
+                continue
+            # ---------- fold the NEWxxx operations into MALLOC_GC ----------
+            if op.is_malloc():
+                self.handle_malloc_operation(op)
+                continue
+            elif op.can_malloc():
+                self.op_last_malloc_gc = None
+                self.current_mallocs.clear()
+            # ---------- write barriers ----------
+            if self.gc_ll_descr.write_barrier_descr is not None:
+                if op.getopnum() == rop.SETFIELD_GC:
+                    op = self.handle_write_barrier_setfield(op)
+                elif op.getopnum() == rop.SETARRAYITEM_GC:
+                    op = self.handle_write_barrier_setarrayitem(op)
+            # ----------
+            self.newops.append(op)
+        return self.newops
+
+    def handle_malloc_operation(self, op):
+        opnum = op.getopnum()
+        if opnum == rop.NEW:
+            descr = op.getdescr()
+            assert isinstance(descr, BaseSizeDescr)
+            c_size = ConstInt(descr.size)
+            c_zero = self.c_zero
+            op = ResOperation(rop.MALLOC_GC, [c_size, c_zero, c_zero],
+                              op.result)
+            self.newops.append(op)
+        elif opnum == rop.NEW_WITH_VTABLE:
+            classint = op.getarg(0).getint()
+            descr = heaptracker.vtable2descr(self.cpu, classint)
+            c_size = ConstInt(descr.size)
+            c_zero = self.c_zero
+            op = ResOperation(rop.MALLOC_GC, [c_size, c_zero, c_zero],
+                              op.result)
+            self.newops.append(op)
+            if self.gc_ll_descr.fielddescr_vtable is not None:
+                op = ResOperation(rop.SETFIELD_GC,
+                                  [op.result, ConstInt(classint)], None,
+                                  descr=self.gc_ll_descr.fielddescr_vtable)
+                self.newops.append(op)
+        elif opnum == rop.NEW_ARRAY:
+            descr = op.getdescr()
+            assert isinstance(descr, BaseArrayDescr)
+            #...
+            c_basesize = ConstInt(descr.get_base_size(self.tsc))
+            c_itemsize = ConstInt(descr.get_item_size(self.tsc))
+            v_length = op.getarg(0)
+            op = ResOperation(rop.MALLOC_GC, [c_basesize,
+                                              v_length,
+                                              c_itemsize],
+                              op.result)
+            self.newops.append(op)
+            op = ResOperation(rop.SETFIELD_GC, [op.result, v_length],
+                              None, descr=descr.field_arraylen_descr)
+            self.newops.append(op)
+        else:
+            raise NotImplementedError(op.getopname())
+
+##                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
+
+    def handle_write_barrier_setfield(self, op):
+        val = op.getarg(0)
+        # no need for a write barrier in the case of previous malloc
+        if val not in self.current_mallocs:
+            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)
+        return op
+
+    def handle_write_barrier_setarrayitem(self, op):
+        val = op.getarg(0)
+        # no need for a write barrier in the case of previous malloc
+        if val not in self.current_mallocs:
+            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)
+        return op
+
+    def gen_malloc_const(self, size, v_result):
+        size = self.round_up_for_allocation(size)
+        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.current_mallocs[v_result] = None
+        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):
+        write_barrier_descr = self.gc_ll_descr.write_barrier_descr
+        args = [v_base, v_value]
+        self.newops.append(ResOperation(rop.COND_CALL_GC_WB, args, None,
+                                        descr=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 round_up_for_allocation(self, size):
+        if self.tsc:
+            return llarena.round_up_for_allocation(
+                size, self.gc_ll_descr.minimal_size_in_nursery)
+        else:
+            # non-translated: do it manually
+            # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs
+            size = max(size, 2 * WORD)
+            return (size + WORD-1) & ~(WORD-1)     # round up


More information about the pypy-commit mailing list