[pypy-commit] pypy op_malloc_gc: Attempt to collapse several NEW-like operations into a single

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


Author: Armin Rigo <arigo at tunes.org>
Branch: op_malloc_gc
Changeset: r49508:a85153701bb6
Date: 2011-11-10 21:32 +0100
http://bitbucket.org/pypy/pypy/changeset/a85153701bb6/

Log:	Attempt to collapse several NEW-like operations into a single
	simpler MALLOC_GC operation: starting...

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
@@ -672,6 +672,7 @@
         self.WB_ARRAY_FUNCPTR = lltype.Ptr(lltype.FuncType(
             [llmemory.Address, lltype.Signed, llmemory.Address], lltype.Void))
         self.write_barrier_descr = WriteBarrierDescr(self)
+        self.fielddescr_tid = self.write_barrier_descr.fielddescr_tid
         #
         def malloc_array(itemsize, tid, num_elem):
             type_id = llop.extract_ushort(llgroup.HALFWORD, tid)
@@ -809,17 +810,52 @@
         newops = []
         known_lengths = {}
         # we can only remember one malloc since the next malloc can possibly
-        # collect
-        last_malloc = None
+        # 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():
-                last_malloc = op.result
+                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():
-                last_malloc = None
+                op_last_malloc = None
             # ---------- write barrier for SETFIELD_GC ----------
             if op.getopnum() == rop.SETFIELD_GC:
                 val = op.getarg(0)
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
@@ -570,6 +570,68 @@
             assert operations[1].getarg(2) == v_value
             assert operations[1].getdescr() == array_descr
 
+    def test_rewrite_assembler_new_to_malloc(self):
+        self.gc_ll_descr.translate_support_code = False
+        try:
+            S = lltype.GcStruct('S', ('x', lltype.Signed))
+            sdescr = get_size_descr(self.gc_ll_descr, S)
+            sdescr.tid = 1234
+        finally:
+            self.gc_ll_descr.translate_support_code = True
+        tiddescr = self.gc_ll_descr.fielddescr_tid
+        ops = parse("""
+        [p1]
+        p0 = new(descr=sdescr)
+        jump()
+        """, namespace=locals())
+        expected = parse("""
+        [p1]
+        p0 = malloc_gc(%d)
+        setfield_gc(p0, 1234, descr=tiddescr)
+        jump()
+        """ % (sdescr.size,), 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_new3_to_malloc(self):
+        self.gc_ll_descr.translate_support_code = False
+        try:
+            S = lltype.GcStruct('S', ('x', lltype.Signed))
+            sdescr = get_size_descr(self.gc_ll_descr, S)
+            sdescr.tid = 1234
+            T = lltype.GcStruct('T', ('y', lltype.Signed),
+                                     ('z', lltype.Signed))
+            tdescr = get_size_descr(self.gc_ll_descr, T)
+            tdescr.tid = 5678
+        finally:
+            self.gc_ll_descr.translate_support_code = True
+        tiddescr = self.gc_ll_descr.fielddescr_tid
+        ops = parse("""
+        []
+        p0 = new(descr=sdescr)
+        p1 = new(descr=tdescr)
+        p2 = new(descr=sdescr)
+        jump()
+        """, namespace=locals())
+        expected = parse("""
+        []
+        p0 = malloc_gc(%d)
+        setfield_gc(p0, 1234, descr=tiddescr)
+        p1 = int_add(p0, %d)
+        setfield_gc(p1, 5678, descr=tiddescr)
+        p2 = int_add(p1, %d)
+        setfield_gc(p2, 1234, descr=tiddescr)
+        jump()
+        """ % (sdescr.size + tdescr.size + sdescr.size,
+               sdescr.size,
+               tdescr.size), 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))
diff --git a/pypy/jit/metainterp/executor.py b/pypy/jit/metainterp/executor.py
--- a/pypy/jit/metainterp/executor.py
+++ b/pypy/jit/metainterp/executor.py
@@ -344,6 +344,7 @@
                          rop.SETINTERIORFIELD_RAW,
                          rop.CALL_RELEASE_GIL,
                          rop.QUASIIMMUT_FIELD,
+                         rop.MALLOC_GC,
                          ):      # list of opcodes never executed by pyjitpl
                 continue
             raise AssertionError("missing %r" % (key,))
diff --git a/pypy/jit/metainterp/resoperation.py b/pypy/jit/metainterp/resoperation.py
--- a/pypy/jit/metainterp/resoperation.py
+++ b/pypy/jit/metainterp/resoperation.py
@@ -470,6 +470,7 @@
     'NEW_ARRAY/1d',
     'NEWSTR/1',
     'NEWUNICODE/1',
+    'MALLOC_GC/1',        # added by llsupport/gc: GC malloc of ConstInt bytes
     '_MALLOC_LAST',
     'FORCE_TOKEN/0',
     'VIRTUAL_REF/2',         # removed before it's passed to the backend


More information about the pypy-commit mailing list