[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