[pypy-svn] r23948 - in pypy/dist/pypy: rpython/memory translator/c translator/c/test
arigo at codespeak.net
arigo at codespeak.net
Fri Mar 3 16:31:50 CET 2006
Author: arigo
Date: Fri Mar 3 16:31:47 2006
New Revision: 23948
Modified:
pypy/dist/pypy/rpython/memory/gc.py
pypy/dist/pypy/rpython/memory/gctransform.py
pypy/dist/pypy/translator/c/database.py
pypy/dist/pypy/translator/c/test/test_newgc.py
Log:
Intermediate check-in: getting close to translating the MarkSweepGC.
Modified: pypy/dist/pypy/rpython/memory/gc.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gc.py (original)
+++ pypy/dist/pypy/rpython/memory/gc.py Fri Mar 3 16:31:47 2006
@@ -87,7 +87,7 @@
def __init__(self, dummy=None, get_roots=None):
self.get_roots = get_roots
- self.set_query_functions(None, None, None, None, None, None, None)
+ #self.set_query_functions(None, None, None, None, None, None, None)
def malloc(self, typeid, length=0):
size = self.fixed_size(typeid)
@@ -115,7 +115,7 @@
#need to maintain a list of malloced objects, since we used the systems
#allocator and can't walk the heap
self.malloced_objects = AddressLinkedList()
- self.set_query_functions(None, None, None, None, None, None, None)
+ #self.set_query_functions(None, None, None, None, None, None, None)
self.get_roots = get_roots
def malloc(self, typeid, length=0):
@@ -224,7 +224,7 @@
self.top_of_space = self.tospace + space_size
self.fromspace = raw_malloc(space_size)
self.free = self.tospace
- self.set_query_functions(None, None, None, None, None, None, None)
+ #self.set_query_functions(None, None, None, None, None, None, None)
self.get_roots = get_roots
def free_memory(self):
@@ -359,7 +359,7 @@
self.zero_ref_counts = AddressLinkedList()
self.length_zero_ref_counts = 0
self.max_refcount_zero = max_refcount_zero
- self.set_query_functions(None, None, None, None, None, None, None)
+ #self.set_query_functions(None, None, None, None, None, None, None)
self.get_roots = get_roots
self.collecting = False
Modified: pypy/dist/pypy/rpython/memory/gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform.py (original)
+++ pypy/dist/pypy/rpython/memory/gctransform.py Fri Mar 3 16:31:47 2006
@@ -88,10 +88,20 @@
for var in block.inputargs:
if var_needsgc(var):
newops.extend(self.push_alive(var))
+ # XXX this is getting obscure. Maybe we should use the basic
+ # graph-transforming capabilities of the RTyper instead, as we
+ # seem to run into all the same problems as the ones we already
+ # had to solve there.
+ num_ops_after_exc_raising = 0
for op in block.operations:
- ops, cleanup_before_exception = self.replacement_operations(op, livevars)
+ num_ops_after_exc_raising = 0
+ res = self.replacement_operations(op, livevars)
+ try:
+ ops, cleanup_before_exception = res
+ except ValueError:
+ ops, cleanup_before_exception, num_ops_after_exc_raising = res
newops.extend(ops)
- op = ops[-1]
+ op = ops[-1-num_ops_after_exc_raising]
# XXX for now we assume that everything can raise
if 1 or op.opname in EXCEPTION_RAISING_OPS:
if tuple(livevars) in livevars2cleanup:
@@ -103,9 +113,12 @@
cleanup_on_exception = tuple(cleanup_on_exception)
livevars2cleanup[tuple(livevars)] = cleanup_on_exception
op.cleanup = tuple(cleanup_before_exception), cleanup_on_exception
+ op = ops[-1]
if var_needsgc(op.result):
if op.opname not in ('direct_call', 'indirect_call') and not var_ispyobj(op.result):
- newops.extend(self.push_alive(op.result))
+ lst = list(self.push_alive(op.result))
+ newops.extend(lst)
+ num_ops_after_exc_raising += len(lst)
livevars.append(op.result)
if len(block.exits) == 0:
# everything is fine already for returnblocks and exceptblocks
@@ -117,6 +130,15 @@
# to the block, even if the variable dies in all
# linked blocks.
deadinallexits = sets.Set([])
+ if num_ops_after_exc_raising > 0:
+ # No place to put the remaining pending operations!
+ # Need a new block along the non-exceptional link.
+ # XXX test this.
+ tail = newops[-num_ops_after_exc_raising:]
+ del newops[-num_ops_after_exc_raising:]
+ link = block.exits[0]
+ assert link.exitcase is None
+ insert_empty_block(self.translator, link, tail)
else:
deadinallexits = sets.Set(livevars)
for link in block.exits:
@@ -608,9 +630,53 @@
def __init__(self, translator):
super(FrameworkGCTransformer, self).__init__(translator)
class GCData(object):
+ from pypy.rpython.memory.gc import MarkSweepGC as GCClass
startheapsize = 640*1024 # XXX adjust
rootstacksize = 640*1024 # XXX adjust
+
+ # types of the GC information tables
+ OFFSETS_TO_GC_PTR = lltype.Array(lltype.Signed)
+ TYPE_INFO = lltype.Struct("type_info",
+ ("fixedsize", lltype.Signed),
+ ("ofstoptrs", lltype.Ptr(OFFSETS_TO_GC_PTR)),
+ ("varitemsize", lltype.Signed),
+ ("ofstovar", lltype.Signed),
+ ("ofstolength", lltype.Signed),
+ ("varofstoptrs",lltype.Ptr(OFFSETS_TO_GC_PTR)),
+ )
+ TYPE_INFO_TABLE = lltype.Array(TYPE_INFO)
+
+ def q_is_varsize(typeid):
+ return gcdata.type_info_table[typeid].varitemsize != 0
+
+ def q_offsets_to_gc_pointers(typeid):
+ return gcdata.type_info_table[typeid].ofstoptrs
+
+ def q_fixed_size(typeid):
+ return gcdata.type_info_table[typeid].fixedsize
+
+ def q_varsize_item_sizes(typeid):
+ return gcdata.type_info_table[typeid].varitemsize
+
+ def q_varsize_offset_to_variable_part(typeid):
+ return gcdata.type_info_table[typeid].ofstovar
+
+ def q_varsize_offset_to_length(typeid):
+ return gcdata.type_info_table[typeid].ofstolength
+
+ def q_varsize_offsets_to_gcpointers_in_var_part(typeid):
+ return gcdata.type_info_table[typeid].varofstoptrs
+
gcdata = GCData()
+ # set up dummy a table, to be overwritten with the real one in finish()
+ gcdata.type_info_table = lltype.malloc(GCData.TYPE_INFO_TABLE, 0,
+ immortal=True)
+ self.gcdata = gcdata
+ self.type_info_list = []
+ self.id_of_type = {} # {LLTYPE: type_id}
+ self.offsettable_cache = {}
+ self.malloc_fnptr_cache = {}
+
sizeofaddr = llmemory.sizeof(llmemory.Address)
from pypy.rpython.memory.lladdress import NULL
@@ -628,11 +694,19 @@
return NULL
def frameworkgc_setup():
+ # run-time initialization code
stackbase = lladdress.raw_malloc(GCData.rootstacksize)
gcdata.root_stack_top = stackbase
gcdata.root_stack_base = stackbase
-# from pypy.rpython.memory.gc import MarkSweepGC
-# gcdata.gc = MarkSweepGC(GCData.startheapsize, StackRootIterator)
+ gcdata.gc = GCData.GCClass(GCData.startheapsize, StackRootIterator)
+ gcdata.gc.set_query_functions(
+ q_is_varsize,
+ q_offsets_to_gc_pointers,
+ q_fixed_size,
+ q_varsize_item_sizes,
+ q_varsize_offset_to_variable_part,
+ q_varsize_offset_to_length,
+ q_varsize_offsets_to_gcpointers_in_var_part)
def push_root(addr):
top = gcdata.root_stack_top
@@ -653,11 +727,20 @@
annmodel.s_None)
pop_root_graph = annhelper.getgraph(pop_root, [],
annmodel.SomeAddress())
+ bk = self.translator.annotator.bookkeeper
+ classdef = bk.getuniqueclassdef(GCData.GCClass)
+ s_gcdata = annmodel.SomeInstance(classdef)
+ malloc_graph = annhelper.getgraph(GCData.GCClass.malloc.im_func,
+ [s_gcdata,
+ annmodel.SomeInteger(nonneg=True),
+ annmodel.SomeInteger(nonneg=True)],
+ annmodel.SomeAddress())
annhelper.finish() # at this point, annotate all mix-level helpers
self.frameworkgc_setup_ptr = self.graph2funcptr(frameworkgc_setup_graph,
attach_empty_cleanup=True)
self.push_root_ptr = self.graph2funcptr(push_root_graph)
self.pop_root_ptr = self.graph2funcptr(pop_root_graph)
+ self.malloc_ptr = self.graph2funcptr(malloc_graph)
def graph2funcptr(self, graph, attach_empty_cleanup=False):
self.seen_graphs[graph] = True
@@ -665,6 +748,71 @@
MinimalGCTransformer(self.translator).transform_graph(graph)
return const_funcptr_fromgraph(graph)
+ def get_type_id(self, TYPE):
+ try:
+ return self.id_of_type[TYPE]
+ except KeyError:
+ assert not self.finished
+ assert isinstance(TYPE, (lltype.GcStruct, lltype.GcArray))
+ # Record the new type_id description as a small dict for now.
+ # It will be turned into a Struct("type_info") in finish()
+ type_id = len(self.type_info_list)
+ info = {}
+ self.type_info_list.append(info)
+ self.id_of_type[TYPE] = type_id
+ offsets = offsets_to_gc_pointers(TYPE)
+ info["ofstoptrs"] = self.offsets2table(offsets)
+ if not TYPE._is_varsize():
+ info["fixedsize"] = llmemory.sizeof(TYPE)
+ else:
+ info["fixedsize"] = llmemory.sizeof(TYPE, 0)
+ if isinstance(TYPE, lltype.Struct):
+ ARRAY = TYPE._flds[TYPE._arrayfld]
+ ofs1 = llmemory.offsetof(TYPE, TYPE._arrayfld)
+ info["ofstolength"] = ofs1
+ info["ofstovar"] = ofs1 + llmemory.itemoffsetof(ARRAY, 0)
+ else:
+ ARRAY = TYPE
+ info["ofstolength"] = 0
+ info["ofstovar"] = llmemory.itemoffsetof(TYPE, 0)
+ assert isinstance(ARRAY, lltype.Array)
+ offsets = offsets_to_gc_pointers(ARRAY.OF)
+ info["varofstoptrs"] = self.offsets2table(offsets)
+ info["varitemsize"] = llmemory.sizeof(ARRAY.OF)
+ return type_id
+
+ def offsets2table(self, offsets):
+ key = tuple(offsets)
+ try:
+ return self.offsettable_cache[key]
+ except KeyError:
+ cachedarray = lltype.malloc(self.gcdata.OFFSETS_TO_GC_PTR,
+ len(offsets), immortal=True)
+ for i, value in enumerate(offsets):
+ cachedarray[i] = value
+ self.offsettable_cache[key] = cachedarray
+ return cachedarray
+
+ def finish(self):
+ newgcdependencies = super(FrameworkGCTransformer, self).finish()
+ if self.type_info_list is not None:
+ table = lltype.malloc(self.gcdata.TYPE_INFO_TABLE,
+ len(self.type_info_list), immortal=True)
+ for tableentry, newcontent in zip(table, self.type_info_list):
+ for key, value in newcontent.items():
+ setattr(tableentry, key, value)
+ self.type_info_list = None
+ self.offsettable_cache = None
+ # replace the type_info_table pointer in gcdata -- at this point,
+ # the database is in principle complete, so it has already seen
+ # the old (empty) array. We need to force it to consider the new
+ # array now. It's a bit hackish as the old empty array will also
+ # be generated in the C source, but that's a rather minor problem.
+ self.gcdata.type_info_table = table
+ newgcdependencies = newgcdependencies or []
+ newgcdependencies.append(table)
+ return newgcdependencies
+
def protect_roots(self, op, livevars):
livevars = [var for var in livevars if not var_ispyobj(var)]
newops = list(self.push_roots(livevars))
@@ -673,8 +821,27 @@
replace_direct_call = protect_roots
replace_indirect_call = protect_roots
- replace_malloc = protect_roots
- replace_malloc_varsize = protect_roots
+
+ def replace_malloc(self, op, livevars):
+ TYPE = op.args[0].value
+ PTRTYPE = op.result.concretetype
+ assert PTRTYPE.TO == TYPE
+ type_id = self.get_type_id(TYPE)
+
+ v = varoftype(llmemory.Address)
+ c_type_id = rmodel.inputconst(lltype.Signed, type_id)
+ if len(op.args) == 1:
+ v_length = rmodel.inputconst(lltype.Signed, 0)
+ else:
+ v_length = op.args[1]
+ newop = SpaceOperation("direct_call",
+ [self.malloc_ptr, c_type_id, v_length],
+ v)
+ ops, finally_ops = self.protect_roots(newop, livevars)
+ ops.append(SpaceOperation("cast_adr_to_ptr", [v], op.result))
+ return ops, finally_ops, 1
+
+ replace_malloc_varsize = replace_malloc
def push_alive_nopyobj(self, var):
return []
@@ -697,6 +864,25 @@
yield SpaceOperation("gc_reload_possibly_moved", [v, var],
varoftype(lltype.Void))
+# XXX copied and modified from lltypelayout.py
+def offsets_to_gc_pointers(TYPE):
+ offsets = []
+ if isinstance(TYPE, lltype.Struct):
+ for name in TYPE._names:
+ FIELD = getattr(TYPE, name)
+ if isinstance(FIELD, lltype.Array):
+ continue # skip inlined array
+ baseofs = llmemory.offsetof(TYPE, name)
+ suboffsets = offsets_to_gc_pointers(FIELD)
+ for s in suboffsets:
+ if s == 0:
+ offsets.append(baseofs)
+ else:
+ offsets.append(baseofs + s)
+ elif (isinstance(TYPE, lltype.Ptr) and TYPE._needsgc() and
+ TYPE.TO is not lltype.PyObject):
+ offsets.append(0)
+ return offsets
# ___________________________________________________________________
# calculate some statistics about the number of variables that need
Modified: pypy/dist/pypy/translator/c/database.py
==============================================================================
--- pypy/dist/pypy/translator/c/database.py (original)
+++ pypy/dist/pypy/translator/c/database.py Fri Mar 3 16:31:47 2006
@@ -178,16 +178,23 @@
if i == show_i:
dump()
show_i += 1000
+ work_to_do = False
if not is_later_yet:
- self.gctransformer.finish()
+ newgcdependencies = self.gctransformer.finish()
+ if newgcdependencies:
+ work_to_do = True
+ for value in newgcdependencies:
+ if isinstance(typeOf(value), ContainerType):
+ self.getcontainernode(value)
+ else:
+ self.get(value)
is_later_yet = True
if self.latercontainerlist:
+ work_to_do = True
for node in self.latercontainerlist:
node.make_funcgen()
self.containerlist.append(node)
self.latercontainerlist = []
- else:
- work_to_do = False
self.completed = True
if show_progress:
dump()
Modified: pypy/dist/pypy/translator/c/test/test_newgc.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_newgc.py (original)
+++ pypy/dist/pypy/translator/c/test/test_newgc.py Fri Mar 3 16:31:47 2006
@@ -193,7 +193,8 @@
class TestUsingFramework(AbstractTestClass):
from pypy.translator.c.gc import FrameworkGcPolicy as gcpolicy
- def test_nongcing_gc(self):
+ def test_framework_simple(self):
+ py.test.skip("in progress, getting there :-)")
def g(x):
return x + 1
class A(object):
More information about the Pypy-commit
mailing list