[pypy-commit] pypy op_malloc_gc: In-progress: found a hopefully reasonable solution, namely a

arigo noreply at buildbot.pypy.org
Sat Dec 17 15:43:51 CET 2011


Author: Armin Rigo <arigo at tunes.org>
Branch: op_malloc_gc
Changeset: r50619:dc4fca252174
Date: 2011-12-17 15:28 +0100
http://bitbucket.org/pypy/pypy/changeset/dc4fca252174/

Log:	In-progress: found a hopefully reasonable solution, namely a
	CALL_MALLOC_GC operation that is basically just a CALL, invoking
	whatever helper rewrite.py wants to introduce.

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
@@ -42,6 +42,15 @@
         self.field_unicodelen_descr = get_field_arraylen_descr(self,
                                                                rstr.UNICODE)
 
+    def _ready(self):
+        MALLOC_FIXEDSIZE = lltype.Ptr(
+            lltype.FuncType([lltype.Signed], llmemory.GCREF))
+        self.malloc_fixedsize_fn = llhelper(MALLOC_FIXEDSIZE,
+                                            self.malloc_fixedsize)
+        self.c_malloc_fixedsize_fn = ConstInt(
+            heaptracker.adr2int(llmemory.cast_ptr_to_adr(
+                self.malloc_fixedsize_fn)))
+
     def _freeze_(self):
         return True
     def initialize(self):
@@ -55,28 +64,11 @@
     def freeing_block(self, start, stop):
         pass
 
-    def get_funcptr_for_malloc_gc_fixed(self):
-        """Returns a function pointer to a function that implements
-        the simple case of MALLOC_GC: the case where the variable size
-        is zero.  The function pointer has signature (size) -> GCREF."""
-        raise NotImplementedError
-
-    def get_funcptr_for_malloc_gc_variable(self):
-        """Returns a function pointer to a function that implements
-        the complex case of MALLOC_GC: the case where the variable size
-        is not known to be zero.  The signature is:
-            (base_size, num_elem, item_size) -> GCREF"""
-        raise NotImplementedError
-
     def gc_malloc(self, sizedescr):
         """Blackhole: do a 'bh_new'.  Also used for 'bh_new_with_vtable',
         with the vtable pointer set manually afterwards."""
         assert isinstance(sizedescr, BaseSizeDescr)
-        mallocptr = self.get_funcptr_for_malloc_gc_fixed()
-        res = mallocptr(sizedescr.size)
-        if res:
-            self._set_tid(res, sizedescr.tid)
-        return res
+        return self._gc_malloc(sizedescr.size, sizedescr.tid)
 
     def gc_malloc_array(self, arraydescr, num_elem):
         assert isinstance(arraydescr, BaseArrayDescr)
@@ -98,18 +90,6 @@
                                      self.unicode_ofs_length,
                                      self.unicode_type_id)
 
-    def _gc_malloc_array(self, basesize, num_elem, itemsize, ofs_length, tid):
-        mallocptr = self.get_funcptr_for_malloc_gc_variable()
-        res = mallocptr(basesize, num_elem, itemsize)
-        if res:
-            self._set_tid(res, tid)
-            arrayptr = rffi.cast(rffi.CArrayPtr(lltype.Signed), res)
-            arrayptr[ofs_length/WORD] = num_elem
-        return res
-
-    def _set_tid(self, gcptr, tid):
-        pass    # unless overridden
-
     def _record_constptrs(self, op, gcrefs_output_list):
         for i in range(op.numargs()):
             v = op.getarg(i)
@@ -180,24 +160,35 @@
     def __init__(self, gcdescr, translator, rtyper):
         GcLLDescription.__init__(self, gcdescr, translator, rtyper)
         # grab a pointer to the Boehm 'malloc' function
-        self.malloc_fn_ptr = self.configure_boehm_once()
+        malloc_fn_ptr = self.configure_boehm_once()
+        self.malloc_fn_ptr = malloc_fn_ptr
         #
-        def malloc_gc_variable(basesize, num_elem, itemsize):
-            try:
-                size = ovfcheck(basesize + ovfcheck(itemsize * num_elem))
-            except OverflowError:
-                return lltype.nullptr(llmemory.GCREF.TO)
-            return self.malloc_fn_ptr(size)
+        def malloc_fixedsize(size):
+            res = malloc_fn_ptr(size)
+            if not res:
+                raise MemoryError
+            return res
+        self.malloc_fixedsize = malloc_fixedsize
         #
-        self.malloc_gc_variable = malloc_gc_variable
-        self.MALLOC_GC_VARIABLE = lltype.Ptr(
-            lltype.FuncType([lltype.Signed] * 3, llmemory.GCREF))
+        self._ready()
 
-    def get_funcptr_for_malloc_gc_fixed(self):
-        return self.malloc_fn_ptr
+    def _gc_malloc(self, size, tid):
+        # Boehm: 'tid' is ignored
+        return self.malloc_fixedsize(size)
 
-    def get_funcptr_for_malloc_gc_variable(self):
-        return llhelper(self.MALLOC_GC_VARIABLE, self.malloc_gc_variable)
+    def _gc_malloc_array(self, basesize, num_elem, itemsize, ofs_length, tid):
+        # Boehm: 'tid' is ignored
+        try:
+            totalsize = ovfcheck(basesize + ovfcheck(itemsize * num_elem))
+        except OverflowError:
+            raise MemoryError
+        res = self.malloc_fn_ptr(totalsize)
+        if not res:
+            raise MemoryError
+        arrayptr = rffi.cast(rffi.CArrayPtr(lltype.Signed), res)
+        arrayptr[ofs_length/WORD] = num_elem
+        return res
+
 
 # ____________________________________________________________
 # All code below is for the hybrid or minimark GC
diff --git a/pypy/jit/backend/llsupport/rewrite.py b/pypy/jit/backend/llsupport/rewrite.py
--- a/pypy/jit/backend/llsupport/rewrite.py
+++ b/pypy/jit/backend/llsupport/rewrite.py
@@ -11,8 +11,11 @@
     #
     # - Remove the DEBUG_MERGE_POINTs.
     #
-    # - Turn all NEW_xxx to MALLOC_GC operations, possibly followed by
-    #   SETFIELDs in order to initialize their GC fields.
+    # - Turn all NEW_xxx to either a CALL_MALLOC_GC, or a CALL_MALLOC_NURSERY
+    #   followed by SETFIELDs in order to initialize their GC fields.  The
+    #   two advantages of CALL_MALLOC_NURSERY is that it inlines the common
+    #   path, and we need only one such operation to allocate several blocks
+    #   of memory at once.
     #
     # - Add COND_CALLs to the write barrier before SETFIELD_GC and
     #   SETARRAYITEM_GC operations.
@@ -40,7 +43,7 @@
         for op in operations:
             if op.getopnum() == rop.DEBUG_MERGE_POINT:
                 continue
-            # ---------- fold the NEWxxx operations into MALLOC_GC ----------
+            # ---------- turn NEWxxx into CALL_MALLOC_xxx ----------
             if op.is_malloc():
                 self.handle_malloc_operation(op)
                 continue
@@ -116,11 +119,12 @@
                 var_size = ovfcheck(item_size * num_elem)
                 total_size = ovfcheck(base_size + var_size)
             except OverflowError:
-                pass
+                pass    # total_size is still -1
         if total_size >= 0:
             self.gen_malloc_nursery(total_size, op.result)
         else:
-            self.gen_malloc_gc(base_size, op.result,
+            xxx
+            self.gen_new_array(base_size, op.result,
                                v_length, ConstInt(item_size))
         self.gen_initialize_tid(op.result, tid)
         self.gen_initialize_len(op.result, v_length, arraylen_descr)
@@ -135,12 +139,14 @@
         self._op_malloc_nursery = None
         self.recent_mallocs.clear()
 
-    def gen_malloc_gc(self, size, v_result,
-                      v_num_elem=c_zero, c_item_size=c_zero):
-        """Generate a MALLOC_GC."""
+    def gen_malloc_fixedsize(self, size, v_result):
+        """Generate a CALL_MALLOC_GC(malloc_fixedsize_fn, Const(size)).
+        Note that with the framework GC, this should be called very rarely.
+        """
+        self.emitting_an_operation_that_can_collect()
         c_size = ConstInt(size)
-        self.emitting_an_operation_that_can_collect()
-        op = ResOperation(rop.MALLOC_GC, [c_size, v_num_elem, c_item_size],
+        op = ResOperation(rop.CALL_MALLOC_GC,
+                          [self.gc_ll_descr.c_malloc_fixedsize_fn, c_size],
                           v_result)
         self.newops.append(op)
         # mark 'v_result' as freshly malloced
@@ -151,8 +157,7 @@
         If that fails, generate a plain MALLOC_GC instead.
         """
         if not self.gc_ll_descr.can_use_nursery_malloc(size):
-            self.gen_malloc_gc(size, v_result)
-            return
+            return self.gen_malloc_fixedsize(size, v_result)
         #
         size = self.round_up_for_allocation(size)
         op = None
@@ -178,6 +183,7 @@
         self._previous_size = size
         self._v_last_malloced_nursery = v_result
         self.recent_mallocs[v_result] = None
+        return True
 
     def gen_initialize_tid(self, v_newgcobj, tid):
         if self.gc_ll_descr.fielddescr_tid is not None:
diff --git a/pypy/jit/backend/llsupport/test/test_rewrite.py b/pypy/jit/backend/llsupport/test/test_rewrite.py
--- a/pypy/jit/backend/llsupport/test/test_rewrite.py
+++ b/pypy/jit/backend/llsupport/test/test_rewrite.py
@@ -15,6 +15,8 @@
 
 class RewriteTests(object):
     def check_rewrite(self, frm_operations, to_operations):
+        malloc_fixedsize = self.gc_ll_descr.malloc_fixedsize_fn
+        #
         S = lltype.GcStruct('S', ('x', lltype.Signed),
                                  ('y', lltype.Signed))
         sdescr = get_size_descr(self.gc_ll_descr, S)
@@ -84,7 +86,7 @@
             jump()
         """, """
             [p1]
-            p0 = malloc_gc(%(sdescr.size)d, 0, 0)
+            p0 = call_malloc_gc(ConstClass(malloc_fixedsize), %(sdescr.size)d)
             jump()
         """)
 
@@ -96,8 +98,8 @@
             jump()
         """, """
             []
-            p0 = malloc_gc(%(sdescr.size)d, 0, 0)
-            p1 = malloc_gc(%(sdescr.size)d, 0, 0)
+            p0 = call_malloc_gc(ConstClass(malloc_fixedsize), %(sdescr.size)d)
+            p1 = call_malloc_gc(ConstClass(malloc_fixedsize), %(sdescr.size)d)
             jump()
         """)
 
@@ -108,8 +110,9 @@
             jump()
         """, """
             []
-            p0 = malloc_gc(%(adescr.get_base_size(False) + \
-                             10 * adescr.get_item_size(False))d, 0, 0)
+            p0 = call_malloc_gc(ConstClass(malloc_fixedsize), \
+                                %(adescr.get_base_size(False) + \
+                                10 * adescr.get_item_size(False))d)
             setfield_gc(p0, 10, descr=alendescr)
             jump()
         """)
@@ -121,8 +124,10 @@
             jump()
         """, """
             [i1]
-            p0 = malloc_gc(%(adescr.get_base_size(False))d,         \
-                           i1, %(adescr.get_item_size(False))d)
+            p0 = call_malloc_gc(ConstClass(malloc_varsize), \
+                                %(adescr.get_base_size(False))d, \
+                                i1, \
+                                %(adescr.get_item_size(False))d)
             setfield_gc(p0, i1, descr=alendescr)
             jump()
         """)
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,8 +344,8 @@
                          rop.SETINTERIORFIELD_RAW,
                          rop.CALL_RELEASE_GIL,
                          rop.QUASIIMMUT_FIELD,
-                         rop.MALLOC_GC,
-                         rop.MALLOC_NURSERY,
+                         rop.CALL_MALLOC_GC,
+                         rop.CALL_MALLOC_NURSERY,
                          rop.LABEL,
                          ):      # list of opcodes never executed by pyjitpl
                 continue
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
@@ -472,8 +472,6 @@
     'NEW_ARRAY/1d',
     'NEWSTR/1',
     'NEWUNICODE/1',
-    'MALLOC_GC/3',       # added by llsupport/gc: malloc of C1+N*C2 bytes
-    'MALLOC_NURSERY/1',  # added by llsupport/gc: nursery malloc, const bytes
     '_MALLOC_LAST',
     'FORCE_TOKEN/0',
     'VIRTUAL_REF/2',         # removed before it's passed to the backend
@@ -510,6 +508,8 @@
     #'OOSEND',                     # ootype operation
     #'OOSEND_PURE',                # ootype operation
     'CALL_PURE/*d',             # removed before it's passed to the backend
+    'CALL_MALLOC_GC/*d',      # like CALL, but NULL => propagate MemoryError
+    'CALL_MALLOC_NURSERY/2d', # nursery malloc, const number of bytes, zeroed
     '_CALL_LAST',
     '_CANRAISE_LAST', # ----- end of can_raise operations -----
 


More information about the pypy-commit mailing list