[pypy-svn] r22661 - in pypy/dist/pypy/rpython/memory: . test
mwh at codespeak.net
mwh at codespeak.net
Wed Jan 25 18:25:53 CET 2006
Author: mwh
Date: Wed Jan 25 18:25:51 2006
New Revision: 22661
Added:
pypy/dist/pypy/rpython/memory/gctransform.py (contents, props changed)
pypy/dist/pypy/rpython/memory/test/test_gctransform.py (contents, props changed)
Log:
(cfbolz, mwh)
A start at a gctransformer that transforms l2 graphs, inserting
(at least potentially) the operations needed by the GC. Some
over complicated tests.
Added: pypy/dist/pypy/rpython/memory/gctransform.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/memory/gctransform.py Wed Jan 25 18:25:51 2006
@@ -0,0 +1,91 @@
+from pypy.rpython.lltypesystem import lltype
+from pypy.objspace.flow.model import SpaceOperation, Variable
+from pypy.translator.unsimplify import insert_empty_block
+import sets
+
+"""
+thought experiments
+
+'setfield' obj field value ->
+ a1 <- 'cast_ptr_to_adr' obj
+ a2 <- 'cast_ptr_to_adr' value
+ 'direct_call' write_barrier a1, offset(TYPE(obj), field), a2
+
+operations that need hooks:
+
+setfield, setarrayitem, direct_call, indirect_call, malloc, getfield,
+getarrayitem, getsubstruct?
+
+push_alive, pop_alive,
+
+"""
+
+def var_needsgc(var):
+ vartype = var.concretetype
+ return isinstance(vartype, lltype.Ptr) and vartype._needsgc()
+
+class GCTransformer:
+ def __init__(self, graphs):
+ self.graphs = graphs
+
+ def transform(self):
+ for graph in self.graphs:
+ self.transform_graph(graph)
+
+ def transform_graph(self, graph):
+ self.links_to_split = {} # link -> vars to pop_alive across the link
+ for block in graph.iterblocks():
+ self.transform_block(block)
+ for link, vars in self.links_to_split.iteritems():
+ newops = []
+ for var in vars:
+ newops.extend(self.pop_alive(var))
+ insert_empty_block(None, link, newops)
+
+ def transform_block(self, block):
+ newops = []
+ livevars = [var for var in block.inputargs if var_needsgc(var)]
+ for op in block.operations:
+ newops.extend(self.replacement_operations(op))
+ if var_needsgc(op.result):
+ if op.opname not in ('direct_call', 'indirect_call'):
+ newops.extend(self.push_alive(op.result))
+ livevars.append(op.result)
+ if len(block.exits) == 0:
+ # everything is fine already for returnblocks and exceptblocks
+ pass
+ elif len(block.exits) == 1:
+ for var in livevars:
+ if var not in block.exits[0].args:
+ newops.extend(self.pop_alive(var))
+ else:
+ deadinallexits = sets.Set(livevars)
+ for link in block.exits:
+ deadinallexits.difference_update(sets.Set(link.args))
+ for var in deadinallexits:
+ newops.extend(self.pop_alive(var))
+ for link in block.exits:
+ deadvarsforlink = sets.Set(livevars) - deadinallexits - sets.Set(link.args)
+ if deadvarsforlink:
+ self.links_to_split[link] = deadvarsforlink
+ if newops:
+ block.operations = newops
+
+ def replacement_operations(self, op):
+ m = getattr(self, 'replace_' + op.opname, None)
+ if m:
+ return m(op)
+ else:
+ return [op]
+
+ def push_alive(self, var):
+ result = Variable()
+ result.concretetype = lltype.Void
+ return [SpaceOperation("push_alive", [var], result)]
+
+ def pop_alive(self, var):
+ result = Variable()
+ result.concretetype = lltype.Void
+ return [SpaceOperation("pop_alive", [var], result)]
+
+
Added: pypy/dist/pypy/rpython/memory/test/test_gctransform.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/rpython/memory/test/test_gctransform.py Wed Jan 25 18:25:51 2006
@@ -0,0 +1,91 @@
+from pypy.rpython.memory import gctransform
+from pypy.translator.translator import TranslationContext, graphof
+from pypy.rpython.lltypesystem import lltype
+
+def rtype_and_transform(func, inputtypes, transformcls):
+ t = TranslationContext()
+ t.buildannotator().build_types(func, inputtypes)
+ t.buildrtyper().specialize(t)
+ transformer = transformcls(t.graphs)
+ transformer.transform()
+ t.checkgraphs()
+ return t
+
+def test_simple():
+ def f():
+ return 1
+ rtype_and_transform(f, [], gctransform.GCTransformer)
+
+def test_fairly_simple():
+ class C:
+ pass
+ def f():
+ c = C()
+ c.x = 1
+ return c.x
+ t = rtype_and_transform(f, [], gctransform.GCTransformer)
+
+def test_return_gcpointer():
+ class C:
+ pass
+ def f():
+ c = C()
+ c.x = 1
+ return c
+ t = rtype_and_transform(f, [], gctransform.GCTransformer)
+
+def test_call_function():
+ class C:
+ pass
+ def f():
+ c = C()
+ c.x = 1
+ return c
+ def g():
+ return f().x
+ t = rtype_and_transform(g, [], gctransform.GCTransformer)
+ ggraph = graphof(t, g)
+ for i, op in enumerate(ggraph.startblock.operations):
+ if op.opname == "direct_call":
+ break
+ else:
+ assert False, "direct_call not found!"
+ assert ggraph.startblock.operations[i + 1].opname != 'push_alive'
+
+def test_multiple_exits():
+ S = lltype.GcStruct("S", ('x', lltype.Signed))
+ T = lltype.GcStruct("T", ('y', lltype.Signed))
+ def f(n):
+ c = lltype.malloc(S)
+ d = lltype.malloc(T)
+ e = lltype.malloc(T)
+ if n:
+ x = d
+ else:
+ x = e
+ return x
+ t = rtype_and_transform(f, [int], gctransform.GCTransformer)
+ fgraph = graphof(t, f)
+ from pypy.translator.backendopt.ssa import SSI_to_SSA
+ SSI_to_SSA(fgraph) # *cough*
+ #t.view()
+ pop_alive_count = 0
+ for i, op in enumerate(fgraph.startblock.operations):
+ if op.opname == "pop_alive":
+ var, = op.args
+ assert var.concretetype == lltype.Ptr(S)
+ pop_alive_count += 1
+
+ assert pop_alive_count == 1, "pop_alive not found!"
+ for link in fgraph.startblock.exits:
+ assert len(link.args) == 2
+ ops = link.target.operations
+ assert len(ops) == 1
+ assert ops[0].opname == 'pop_alive'
+ assert len(ops[0].args) == len(link.target.exits) == \
+ len(link.target.exits[0].args) == 1
+ dyingname = ops[0].args[0].name
+ passedname = link.target.exits[0].args[0].name
+ assert dyingname != passedname
+
+
More information about the Pypy-commit
mailing list