[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