[pypy-svn] r23383 - in pypy/dist/pypy: objspace/flow rpython/memory rpython/memory/test translator/c translator/tool
cfbolz at codespeak.net
cfbolz at codespeak.net
Wed Feb 15 23:52:42 CET 2006
Author: cfbolz
Date: Wed Feb 15 23:52:39 2006
New Revision: 23383
Modified:
pypy/dist/pypy/objspace/flow/model.py
pypy/dist/pypy/rpython/memory/gctransform.py
pypy/dist/pypy/rpython/memory/test/test_gctransform.py
pypy/dist/pypy/translator/c/funcgen.py
pypy/dist/pypy/translator/tool/make_dot.py
Log:
(arigo, cfbolz):
Started to work on the FrameworkGCTransformer. It inserts matching
gc_push_roots/gc_pop_roots operations around calls and mallocs.
We had to extend the 'cleanup' model (again :-/); now an operation's cleanup is
a tuple (finally-operations, except-operations), where the former are always
run after the operation even if it raises, and the latter are run *only* if it
raises.
The Pygame viewer now displays these operations too.
Modified: pypy/dist/pypy/objspace/flow/model.py
==============================================================================
--- pypy/dist/pypy/objspace/flow/model.py (original)
+++ pypy/dist/pypy/objspace/flow/model.py Wed Feb 15 23:52:39 2006
@@ -191,10 +191,20 @@
txt = "%s(%s)" % (txt, self.exitswitch)
return txt
+ def reallyalloperations(self):
+ "Iterate over all operations, including cleanup sub-operations."
+ for op in self.operations:
+ yield op
+ cleanup = getattr(op, 'cleanup', None)
+ if cleanup is not None:
+ for lst in cleanup:
+ for subop in lst:
+ yield subop
+
def getvariables(self):
"Return all variables mentioned in this Block."
result = self.inputargs[:]
- for op in self.operations:
+ for op in self.reallyalloperations():
result += op.args
result.append(op.result)
return uniqueitems([w for w in result if isinstance(w, Variable)])
@@ -202,7 +212,7 @@
def getconstants(self):
"Return all constants mentioned in this Block."
result = self.inputargs[:]
- for op in self.operations:
+ for op in self.reallyalloperations():
result += op.args
return uniqueitems([w for w in result if isinstance(w, Constant)])
@@ -210,7 +220,7 @@
for a in mapping:
assert isinstance(a, Variable), a
self.inputargs = [mapping.get(a, a) for a in self.inputargs]
- for op in self.operations:
+ for op in self.reallyalloperations():
op.args = [mapping.get(a, a) for a in op.args]
op.result = mapping.get(op.result, op.result)
self.exitswitch = mapping.get(self.exitswitch, self.exitswitch)
@@ -334,6 +344,9 @@
self.result = result # either Variable or Constant instance
self.offset = offset # offset in code string
if cleanup is not NOCLEANUP:
+ # None or a pair of tuples of operations:
+ # 0. operations to perform in all cases after 'self'
+ # 1. more operations to perform only in case of exception
self.cleanup = cleanup
def __eq__(self, other):
@@ -349,7 +362,17 @@
return hash((self.opname,tuple(self.args),self.result))
def __repr__(self):
- return "%r = %s(%s)" % (self.result, self.opname, ", ".join(map(repr, self.args)))
+ s = "%r = %s(%s)" % (self.result, self.opname, ", ".join(map(repr, self.args)))
+ cleanup = getattr(self, 'cleanup', None)
+ if cleanup is not None:
+ lines = [s]
+ for case, lst in zip((" finally: ", " except: "), cleanup):
+ indent = case
+ for subop in lst:
+ lines.append(indent + repr(subop))
+ indent = ' ' * len(indent)
+ s = '\n'.join(lines)
+ return s
def __reduce_ex__(self, *args):
# avoid lots of useless list entities
Modified: pypy/dist/pypy/rpython/memory/gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/gctransform.py (original)
+++ pypy/dist/pypy/rpython/memory/gctransform.py Wed Feb 15 23:52:39 2006
@@ -100,7 +100,9 @@
if var_needsgc(var):
newops.extend(self.push_alive(var))
for op in block.operations:
- newops.extend(self.replacement_operations(op))
+ ops, cleanup_before_exception = self.replacement_operations(op, livevars)
+ newops.extend(ops)
+ op = ops[-1]
# XXX for now we assume that everything can raise
if 1 or op.opname in EXCEPTION_RAISING_OPS:
if tuple(livevars) in livevars2cleanup:
@@ -111,7 +113,7 @@
cleanup_on_exception.extend(self.pop_alive(var))
cleanup_on_exception = tuple(cleanup_on_exception)
livevars2cleanup[tuple(livevars)] = cleanup_on_exception
- op.cleanup = cleanup_on_exception
+ op.cleanup = tuple(cleanup_before_exception), cleanup_on_exception
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))
@@ -154,12 +156,12 @@
if newops:
block.operations = newops
- def replacement_operations(self, op):
+ def replacement_operations(self, op, livevars):
m = getattr(self, 'replace_' + op.opname, None)
if m:
- return m(op)
+ return m(op, livevars)
else:
- return [op]
+ return [op], []
def push_alive(self, var):
@@ -342,29 +344,27 @@
varoftype(lltype.Void), cleanup=None))
return result
- def replace_setfield(self, op):
+ def replace_setfield(self, op, livevars):
if not var_needsgc(op.args[2]):
- return [op]
+ return [op], []
oldval = varoftype(op.args[2].concretetype)
getoldvalop = SpaceOperation("getfield",
[op.args[0], op.args[1]], oldval)
result = [getoldvalop]
result.extend(self.push_alive(op.args[2]))
result.append(op)
- result.extend(self.pop_alive(oldval))
- return result
+ return result, self.pop_alive(oldval)
- def replace_setarrayitem(self, op):
+ def replace_setarrayitem(self, op, livevars):
if not var_needsgc(op.args[2]):
- return [op]
+ return [op], []
oldval = varoftype(op.args[2].concretetype)
getoldvalop = SpaceOperation("getarrayitem",
[op.args[0], op.args[1]], oldval)
result = [getoldvalop]
result.extend(self.push_alive(op.args[2]))
result.append(op)
- result.extend(self.pop_alive(oldval))
- return result
+ return result, self.pop_alive(oldval)
def get_rtti(self, TYPE):
if isinstance(TYPE, lltype.GcStruct):
@@ -627,12 +627,46 @@
else:
self.finalizer_funcptrs[TYPE] = None
return None
-
+
+class FrameworkGCTransformer(BoehmGCTransformer):
+ #def __init__(self, translator):
+ # super(FrameworkGCTransformer, self).__init__(translator)
+
+ def protect_roots(self, op, livevars):
+ livevars = [var for var in livevars if not var_ispyobj(var)]
+ newops = self.push_roots(livevars)
+ newops.append(op)
+ return newops, self.pop_roots(livevars)
+
+ replace_direct_call = protect_roots
+ replace_indirect_call = protect_roots
+ replace_malloc = protect_roots
+ replace_malloc_varsize = protect_roots
+
+ def push_alive_nopyobj(self, var):
+ return []
+
+ def pop_alive_nopyobj(self, var):
+ return []
+
+ def push_roots(self, vars):
+ if vars:
+ return [SpaceOperation("gc_push_roots", vars, varoftype(lltype.Void))]
+ else:
+ return []
+
+ def pop_roots(self, vars):
+ if vars:
+ return [SpaceOperation("gc_pop_roots", vars, varoftype(lltype.Void))]
+ else:
+ return []
+
+
# ___________________________________________________________________
# calculate some statistics about the number of variables that need
# to be cared for across a call
-relevant_ops = ["direct_call", "indirect_call", "malloc"]
+relevant_ops = ["direct_call", "indirect_call", "malloc", "malloc_varsize"]
def filter_for_ptr(arg):
return isinstance(arg.concretetype, lltype.Ptr)
Modified: pypy/dist/pypy/rpython/memory/test/test_gctransform.py
==============================================================================
--- pypy/dist/pypy/rpython/memory/test/test_gctransform.py (original)
+++ pypy/dist/pypy/rpython/memory/test/test_gctransform.py Wed Feb 15 23:52:39 2006
@@ -158,8 +158,8 @@
ggraph = graphof(t, g)
direct_calls = [op for op in ggraph.startblock.operations if op.opname == "direct_call"]
assert len(direct_calls) == 3
- assert direct_calls[1].cleanup[0].args[0] == direct_calls[0].result
- assert [op.args[0] for op in direct_calls[2].cleanup] == \
+ assert direct_calls[1].cleanup[1][0].args[0] == direct_calls[0].result
+ assert [op.args[0] for op in direct_calls[2].cleanup[1]] == \
[direct_calls[0].result, direct_calls[1].result]
def test_multiply_passed_var():
@@ -563,3 +563,30 @@
rel = gctransform.relevant_gcvars(t, gctransform.filter_for_nongcptr)
print rel
print sum(rel) / float(len(rel)), max(rel), min(rel)
+
+# ______________________________________________________________________
+# tests for FrameworkGCTransformer
+
+def test_framework_simple():
+ def g(x):
+ return x + 1
+ class A(object):
+ pass
+ def f():
+ a = A()
+ a.b = g(1)
+ return a.b
+ t, transformer = rtype_and_transform(f, [], gctransform.FrameworkGCTransformer, check=False)
+ graph = graphof(t, f)
+ calls = [(i, op) for i, op in enumerate(graph.startblock.operations)
+ if op.opname == 'direct_call' and len(op.args) == 2]
+ assert len(calls) == 1
+ [(i, op)] = calls
+ finallyops, exceptops = op.cleanup
+ assert len(finallyops) == 1
+ assert len(exceptops) == 0
+ pushop = graph.startblock.operations[i-1]
+ [popop] = finallyops
+ assert pushop.opname == "gc_push_roots"
+ assert popop.opname == "gc_pop_roots"
+ assert pushop.args == popop.args
Modified: pypy/dist/pypy/translator/c/funcgen.py
==============================================================================
--- pypy/dist/pypy/translator/c/funcgen.py (original)
+++ pypy/dist/pypy/translator/c/funcgen.py Wed Feb 15 23:52:39 2006
@@ -55,7 +55,7 @@
if hasattr(op, "cleanup"):
if op.cleanup is None:
continue
- for cleanupop in op.cleanup:
+ for cleanupop in op.cleanup[0] + op.cleanup[1]:
mix.extend(cleanupop.args)
mix.append(cleanupop.result)
for link in block.exits:
@@ -184,6 +184,11 @@
err = 'err%d_%d' % (myblocknum, i)
for line in self.gen_op(op, err):
yield line
+ cleanup = getattr(op, 'cleanup', None)
+ if cleanup is not None:
+ for subop in op.cleanup[0]:
+ for line in self.gen_op(subop, "should_never_be_jumped_to2"):
+ yield line
fallthrough = False
if len(block.exits) == 0:
if len(block.inputargs) == 2: # exc_cls, exc_value
@@ -303,10 +308,10 @@
for i, op in list(enumerate(block.operations))[::-1]:
if getattr(op, 'cleanup', None) is None:
continue
- errorcases.setdefault(op.cleanup, []).append(i)
+ errorcases.setdefault(op.cleanup[1], []).append(i)
if fallthrough:
- firstclean = tuple(block.operations[-1].cleanup)
+ firstclean = tuple(block.operations[-1].cleanup[1])
first = errorcases[firstclean]
del errorcases[firstclean]
first.remove(len(block.operations) - 1)
Modified: pypy/dist/pypy/translator/tool/make_dot.py
==============================================================================
--- pypy/dist/pypy/translator/tool/make_dot.py (original)
+++ pypy/dist/pypy/translator/tool/make_dot.py Wed Feb 15 23:52:39 2006
@@ -118,7 +118,9 @@
def visit_Block(self, block):
# do the block itself
name = self.blockname(block)
- lines = map(repr, block.operations)
+ lines = []
+ for op in block.operations:
+ lines.extend(repr(op).split('\n'))
lines.append("")
numblocks = len(block.exits)
color = "black"
More information about the Pypy-commit
mailing list