[pypy-svn] r24656 - in pypy/dist/pypy/translator: backendopt c c/test
cfbolz at codespeak.net
cfbolz at codespeak.net
Tue Mar 21 00:22:03 CET 2006
Author: cfbolz
Date: Tue Mar 21 00:21:56 2006
New Revision: 24656
Modified:
pypy/dist/pypy/translator/backendopt/inline.py
pypy/dist/pypy/translator/c/exceptiontransform.py
pypy/dist/pypy/translator/c/test/test_exceptiontransform.py
Log:
make catching of exceptions and raising work. requires an evil hack in the inliner
Modified: pypy/dist/pypy/translator/backendopt/inline.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/inline.py (original)
+++ pypy/dist/pypy/translator/backendopt/inline.py Tue Mar 21 00:21:56 2006
@@ -102,7 +102,8 @@
return False
class Inliner(object):
- def __init__(self, translator, graph, inline_func, inline_guarded_calls=False):
+ def __init__(self, translator, graph, inline_func, inline_guarded_calls=False,
+ inline_guarded_calls_no_matter_what=False):
self.translator = translator
self.graph = graph
self.inline_func = inline_func
@@ -113,6 +114,9 @@
for g, block, i in callsites:
self.block_to_index.setdefault(block, {})[i] = g
self.inline_guarded_calls = inline_guarded_calls
+ # if this argument is set, the inliner will happily produce wrong code!
+ # it is used by the exception transformation
+ self.inline_guarded_calls_no_matter_what = inline_guarded_calls_no_matter_what
if inline_guarded_calls:
self.raise_analyzer = canraise.RaiseAnalyzer(translator)
else:
@@ -151,7 +155,8 @@
index_operation == len(block.operations) - 1):
self.exception_guarded = True
if self.inline_guarded_calls:
- if does_raise_directly(self.graph_to_inline, self.raise_analyzer):
+ if (not self.inline_guarded_calls_no_matter_what and
+ does_raise_directly(self.graph_to_inline, self.raise_analyzer)):
raise CannotInline("can't inline because the call is exception guarded")
elif len(collect_called_graphs(self.graph_to_inline, self.translator)) != 0:
raise CannotInline("can't handle exceptions")
Modified: pypy/dist/pypy/translator/c/exceptiontransform.py
==============================================================================
--- pypy/dist/pypy/translator/c/exceptiontransform.py (original)
+++ pypy/dist/pypy/translator/c/exceptiontransform.py Tue Mar 21 00:21:56 2006
@@ -1,7 +1,7 @@
-from pypy.translator.unsimplify import split_block
-from pypy.translator.backendopt import canraise
+from pypy.translator.unsimplify import copyvar, split_block
+from pypy.translator.backendopt import canraise, inline
from pypy.objspace.flow.model import Block, Constant, Variable, Link, \
- c_last_exception, SpaceOperation, checkgraph
+ c_last_exception, SpaceOperation, checkgraph, FunctionGraph
from pypy.rpython.lltypesystem import lltype, llmemory
from pypy.rpython.memory.lladdress import NULL
from pypy.rpython import rclass
@@ -18,36 +18,94 @@
def error_value(T):
if isinstance(T, lltype.Primitive):
return Constant(PrimitiveErrorValue[T], T)
- elif isinstance(T, Ptr):
+ elif isinstance(T, lltype.Ptr):
return Constant(None, T)
assert 0, "not implemented yet"
+# dummy functions to make the resulting graphs runnable on the llinterpreter
+
+class ExcData(object):
+ exc_type = None
+ exc_value = None
+
+def rpyexc_occured():
+ return ExcData.exc_type is not None
+
+def rpyexc_fetch_type():
+ return ExcData.exc_type
+
+def rpyexc_fetch_value():
+ return ExcData.exc_value
+
+def rpyexc_clear():
+ ExcData.exc_type = None
+ ExcData.exc_value = None
+
+def rpyexc_raise(etype, evalue):
+ ExcData.exc_type = etype
+ ExcData.exc_value = evalue
+
class ExceptionTransformer(object):
def __init__(self, translator):
self.translator = translator
self.raise_analyzer = canraise.RaiseAnalyzer(translator)
+ self.exc_data = translator.rtyper.getexceptiondata()
RPYEXC_OCCURED_TYPE = lltype.FuncType([], lltype.Bool)
self.rpyexc_occured_ptr = Constant(lltype.functionptr(
- RPYEXC_OCCURED_TYPE, "RPyExceptionOccurred", external="C"),
+ RPYEXC_OCCURED_TYPE, "RPyExceptionOccurred", external="C",
+ neverrraises=True, _callable=rpyexc_occured),
lltype.Ptr(RPYEXC_OCCURED_TYPE))
+ RPYEXC_FETCH_TYPE_TYPE = lltype.FuncType([], self.exc_data.lltype_of_exception_type)
+ self.rpyexc_fetch_type_ptr = Constant(lltype.functionptr(
+ RPYEXC_FETCH_TYPE_TYPE, "RPyFetchExceptionType", external="C",
+ neverraises=True, _callable=rpyexc_fetch_type),
+ lltype.Ptr(RPYEXC_FETCH_TYPE_TYPE))
+ RPYEXC_FETCH_VALUE_TYPE = lltype.FuncType([], self.exc_data.lltype_of_exception_value)
+ self.rpyexc_fetch_value_ptr = Constant(lltype.functionptr(
+ RPYEXC_FETCH_VALUE_TYPE, "RPyFetchExceptionValue", external="C",
+ neverraises=True, _callable=rpyexc_fetch_value),
+ lltype.Ptr(RPYEXC_FETCH_VALUE_TYPE))
+ RPYEXC_CLEAR = lltype.FuncType([], lltype.Void)
+ self.rpyexc_clear_ptr = Constant(lltype.functionptr(
+ RPYEXC_CLEAR, "RPyClearException", external="C",
+ neverraises=True, _callable=rpyexc_clear),
+ lltype.Ptr(RPYEXC_CLEAR))
+ RPYEXC_RAISE = lltype.FuncType([self.exc_data.lltype_of_exception_type,
+ self.exc_data.lltype_of_exception_value],
+ lltype.Void)
+ self.rpyexc_raise_ptr = Constant(lltype.functionptr(
+ RPYEXC_RAISE, "RPyRaiseException", external="C",
+ neverraises=True, _callable=rpyexc_raise),
+ lltype.Ptr(RPYEXC_RAISE))
+
+ def transform_completely(self):
+ for graph in self.translator.graphs:
+ self.create_exception_handling(graph)
def create_exception_handling(self, graph):
"""After an exception in a direct_call (or indirect_call), that is not caught
by an explicit
except statement, we need to reraise the exception. So after this
direct_call we need to test if an exception had occurred. If so, we return
- from the current graph with an unused value (false/0/0.0/null).
+ from the current graph with a special value (False/-1/-1.0/null).
Because of the added exitswitch we need an additional block.
"""
- exc_data = self.translator.rtyper.getexceptiondata()
for block in list(graph.iterblocks()): #collect the blocks before changing them
self.transform_block(graph, block)
checkgraph(graph)
def transform_block(self, graph, block):
- last_operation = len(block.operations)-1
+ if block is graph.exceptblock:
+ self.transform_except_block(graph, block)
+ return
+ elif block is graph.returnblock:
+ return
+ last_operation = len(block.operations) - 1
if block.exitswitch == c_last_exception:
+ need_exc_matching = True
last_operation -= 1
+ else:
+ need_exc_matching = False
for i in range(last_operation, -1, -1):
op = block.operations[i]
print "considering op", op, i
@@ -56,19 +114,105 @@
afterblock = split_block(self.translator, graph, block, i+1)
- var_exc_occured = Variable()
- var_exc_occured.concretetype = lltype.Bool
-
- block.operations.append(SpaceOperation("direct_call", [self.rpyexc_occured_ptr], var_exc_occured))
- block.exitswitch = var_exc_occured
+ var_exc_occured, block = self.gen_exc_checking_var(op, i, block, graph)
#non-exception case
block.exits[0].exitcase = block.exits[0].llexitcase = False
+ if need_exc_matching:
+ if not self.raise_analyzer.can_raise(op):
+ print "XXX: operation %s cannot raise, but has exception guarding in graph %s" (op, graph)
+ block.exitswitch = None
+ block.exits = [block.exits[0]]
+ else:
+ self.insert_matching(afterblock, graph)
+
+ def transform_except_block(self, graph, block):
+ # attach an except block -- let's hope that nobody uses it
+ graph.exceptblock = Block([Variable('etype'), # exception class
+ Variable('evalue')]) # exception value
+ result = Variable()
+ result.concretetype = lltype.Void
+ block.operations = [SpaceOperation(
+ "direct_call", [self.rpyexc_raise_ptr] + block.inputargs, result)]
+ l = Link([error_value(graph.returnblock.inputargs[0].concretetype)], graph.returnblock)
+ l.prevblock = block
+ block.exits = [l]
+
+ def insert_matching(self, block, graph):
+ proxygraph, op = self.create_proxy_graph(block.operations[-1])
+ block.operations[-1] = op
+ #non-exception case
+ block.exits[0].exitcase = block.exits[0].llexitcase = None
+ # use the dangerous second True flag :-)
+ inliner = inline.Inliner(self.translator, graph, proxygraph, True, True)
+ inliner.inline_all()
+ block.exits[0].exitcase = block.exits[0].llexitcase = False
+
+ def create_proxy_graph(self, op):
+ """ creates a graph which calls the original function, checks for
+ raised exceptions, fetches and then raises them again. If this graph is
+ inlined, the correct exception matching blocks are produced."""
+ # XXX slightly annoying: construct a graph by hand
+ # but better than the alternative
+ result = copyvar(self.translator, op.result)
+ opargs = []
+ inputargs = []
+ callargs = []
+ ARGTYPES = []
+ for var in op.args:
+ if isinstance(var, Variable):
+ v = Variable()
+ v.concretetype = var.concretetype
+ inputargs.append(v)
+ opargs.append(v)
+ callargs.append(var)
+ ARGTYPES.append(var.concretetype)
+ else:
+ opargs.append(var)
+ newop = SpaceOperation(op.opname, opargs, result)
+ startblock = Block(inputargs)
+ startblock.operations.append(newop)
+ newgraph = FunctionGraph("dummy", startblock)
+ startblock.closeblock(Link([result], newgraph.returnblock))
+ startblock.exits = list(startblock.exits)
+ newgraph.returnblock.inputargs[0].concretetype = op.result.concretetype
+ var_exc_occured, block = self.gen_exc_checking_var(newop, 0, startblock, newgraph)
+ startblock.exits[0].exitcase = startblock.exits[0].llexitcase = False
+ excblock = Block([])
+ var_value = Variable()
+ var_value.concretetype = self.exc_data.lltype_of_exception_value
+ var_type = Variable()
+ var_type.concretetype = self.exc_data.lltype_of_exception_type
+ var_void = Variable()
+ var_void.concretetype = lltype.Void
+ excblock.operations.append(SpaceOperation(
+ "direct_call", [self.rpyexc_fetch_value_ptr], var_value))
+ excblock.operations.append(SpaceOperation(
+ "direct_call", [self.rpyexc_fetch_type_ptr], var_type))
+ excblock.operations.append(SpaceOperation(
+ "direct_call", [self.rpyexc_clear_ptr], var_void))
+ newgraph.exceptblock.inputargs[0].concretetype = self.exc_data.lltype_of_exception_type
+ newgraph.exceptblock.inputargs[1].concretetype = self.exc_data.lltype_of_exception_value
+ excblock.closeblock(Link([var_type, var_value], newgraph.exceptblock))
+ block.exits[True].target = excblock
+ block.exits[True].args = []
+ FUNCTYPE = lltype.FuncType(ARGTYPES, op.result.concretetype)
+ fptr = Constant(lltype.functionptr(FUNCTYPE, "dummy", graph=newgraph),
+ lltype.Ptr(FUNCTYPE))
+ self.translator.graphs.append(newgraph)
+ return newgraph, SpaceOperation("direct_call", [fptr] + callargs, op.result)
+
+ def gen_exc_checking_var(self, op, i, block, graph):
+ var_exc_occured = Variable()
+ var_exc_occured.concretetype = lltype.Bool
+
+ block.operations.append(SpaceOperation("direct_call", [self.rpyexc_occured_ptr], var_exc_occured))
+ block.exitswitch = var_exc_occured
+ #exception occurred case
+ l = Link([error_value(graph.returnblock.inputargs[0].concretetype)], graph.returnblock)
+ l.prevblock = block
+ l.exitcase = l.llexitcase = True
- #exception occurred case
- l = Link([error_value(graph.returnblock.inputargs[0].concretetype)], graph.returnblock)
- l.prevblock = block
- l.exitcase = l.llexitcase = True
-
- block.exits.append(l)
+ block.exits.append(l)
+ return var_exc_occured, block
Modified: pypy/dist/pypy/translator/c/test/test_exceptiontransform.py
==============================================================================
--- pypy/dist/pypy/translator/c/test/test_exceptiontransform.py (original)
+++ pypy/dist/pypy/translator/c/test/test_exceptiontransform.py Tue Mar 21 00:21:56 2006
@@ -1,6 +1,8 @@
from pypy.translator.translator import TranslationContext, graphof
+from pypy.translator.simplify import join_blocks
from pypy.translator.c import exceptiontransform
from pypy.objspace.flow.model import c_last_exception
+from pypy.rpython.test.test_llinterp import get_interpreter
from pypy import conftest
@@ -13,22 +15,36 @@
g = graphof(t, fn)
etrafo = exceptiontransform.ExceptionTransformer(t)
etrafo.create_exception_handling(g)
+ join_blocks(g)
if conftest.option.view:
t.view()
return t, g
+_already_transformed = {}
+
+def interpret(func, values):
+ interp, graph = get_interpreter(func, values)
+ t = interp.typer.annotator.translator
+ if t not in _already_transformed:
+ etrafo = exceptiontransform.ExceptionTransformer(t)
+ etrafo.transform_completely()
+ _already_transformed[t] = True
+ return interp.eval_graph(graph, values)
+
def test_simple():
def one():
return 1
def foo():
one()
- one()
+ return one()
t, g = transform_func(foo, [])
assert len(list(g.iterblocks())) == 2 # graph does not change
+ result = interpret(foo, [])
+ assert result == 1
-def test_raises():
+def test_passthrough():
def one(x):
if x:
raise ValueError()
@@ -39,3 +55,40 @@
t, g = transform_func(foo, [])
assert len(list(g.iterblocks())) == 4
+def test_catches():
+ def one(x):
+ if x == 1:
+ raise ValueError()
+ elif x == 2:
+ raise TypeError()
+ return x - 5
+
+ def foo(x):
+ x = one(x)
+ try:
+ x = one(x)
+ except ValueError:
+ return 1 + x
+ except TypeError:
+ return 2 + x
+ except:
+ return 3 + x
+ return 4 + x
+ t, g = transform_func(foo, [int])
+ assert len(list(g.iterblocks())) == 9
+ result = interpret(foo, [6])
+ assert result == 2
+ result = interpret(foo, [7])
+ assert result == 4
+ result = interpret(foo, [8])
+ assert result == 2
+
+
+def test_raises():
+ def foo(x):
+ if x:
+ raise ValueError()
+ t, g = transform_func(foo, [int])
+ assert len(list(g.iterblocks())) == 4
+
+
More information about the Pypy-commit
mailing list