[pypy-svn] r62898 - in pypy/branch/pyjitpl5/pypy/translator: . test
arigo at codespeak.net
arigo at codespeak.net
Thu Mar 12 18:40:17 CET 2009
Author: arigo
Date: Thu Mar 12 18:40:17 2009
New Revision: 62898
Modified:
pypy/branch/pyjitpl5/pypy/translator/simplify.py
pypy/branch/pyjitpl5/pypy/translator/test/test_simplify.py
Log:
Attempt to rewrite from scratch transform_ovfcheck().
Hopefully I've written enough tests to cover all cases...
The point is that test_remove_ovfcheck_bug() used to crash.
Modified: pypy/branch/pyjitpl5/pypy/translator/simplify.py
==============================================================================
--- pypy/branch/pyjitpl5/pypy/translator/simplify.py (original)
+++ pypy/branch/pyjitpl5/pypy/translator/simplify.py Thu Mar 12 18:40:17 2009
@@ -101,124 +101,51 @@
"""The special function calls ovfcheck and ovfcheck_lshift need to
be translated into primitive operations. ovfcheck is called directly
after an operation that should be turned into an overflow-checked
- version. It is considered a syntax error if the resulting <op>-ovf
- is not defined in baseobjspace.py .
+ version. It is considered a syntax error if the resulting <op>_ovf
+ is not defined in objspace/flow/objspace.py.
ovfcheck_lshift is special because there is no preceding operation.
Instead, it will be replaced by an OP_LSHIFT_OVF operation.
-
- The exception handling of the original operation is completely
- ignored. Only exception handlers for the ovfcheck function call
- are taken into account. This gives us the best possible control
- over situations where we want exact contol over certain operations.
- Example:
-
- try:
- array1[idx-1] = ovfcheck(array1[idx-1] + array2[idx+1])
- except OverflowError:
- ...
-
- assuming two integer arrays, we are only checking the element addition
- for overflows, but the indexing is not checked.
"""
- # General assumption:
- # empty blocks have been eliminated.
- # ovfcheck can appear in the same block with its operation.
- # this is the case if no exception handling was provided.
- # Otherwise, we have a block ending in the operation,
- # followed by a block with a single ovfcheck call.
from pypy.rlib.rarithmetic import ovfcheck, ovfcheck_lshift
- from pypy.objspace.flow.objspace import op_appendices
from pypy.objspace.flow.objspace import implicit_exceptions
covf = Constant(ovfcheck)
covfls = Constant(ovfcheck_lshift)
- appendix = op_appendices[OverflowError]
- renaming = {}
- seen_ovfblocks = {}
-
- # get all blocks
- blocks = {}
- def visit(block):
- if isinstance(block, Block):
- blocks[block] = True
- traverse(visit, graph)
- def is_ovfcheck(bl):
- ops = bl.operations
- return (ops and ops[-1].opname == "simple_call"
- and ops[-1].args[0] == covf)
- def is_ovfshiftcheck(bl):
- ops = bl.operations
- return (ops and ops[-1].opname == "simple_call"
- and ops[-1].args[0] == covfls)
- def is_single(bl):
- return is_ovfcheck(bl) and len(bl.operations) > 1
- def is_paired(bl):
- if bl.exits:
- ovfblock = bl.exits[0].target
- return (bl.exits and is_ovfcheck(ovfblock) and
- len(ovfblock.operations) == 1)
- def rename(v):
- return renaming.get(v, v)
- def remove_last_op(bl):
- delop = bl.operations.pop()
- assert delop.opname == "simple_call"
- assert len(delop.args) == 2
- renaming[delop.result] = rename(delop.args[1])
- for exit in bl.exits:
- exit.args = [rename(a) for a in exit.args]
-
- def check_syntax(ovfblock, block=None):
- """check whether ovfblock is reachable more than once
- or if they cheated about the argument"""
- if block:
- link = block.exits[0]
- for lprev, ltarg in zip(link.args, ovfblock.inputargs):
- renaming[ltarg] = rename(lprev)
- arg = ovfblock.operations[0].args[-1]
- res = block.operations[-1].result
- opname = block.operations[-1].opname
- else:
- arg = ovfblock.operations[-1].args[-1]
- res = ovfblock.operations[-2].result
- opname = ovfblock.operations[-2].opname
- if rename(arg) != rename(res) or ovfblock in seen_ovfblocks:
- raise SyntaxError("ovfcheck in %s: The checked operation %s"
- " is misplaced" % (graph.name, opname))
- exlis = implicit_exceptions.get("%s_%s" % (opname, appendix), [])
+ def check_syntax(opname):
+ exlis = implicit_exceptions.get("%s_ovf" % (opname,), [])
if OverflowError not in exlis:
- raise SyntaxError("ovfcheck in %s: Operation %s has no"
- " overflow variant" % (graph.name, opname))
+ raise Exception("ovfcheck in %s: Operation %s has no"
+ " overflow variant" % (graph.name, opname))
- blocks_to_join = False
- for block in blocks:
- if is_ovfshiftcheck(block):
- # ovfcheck_lshift:
- # simply rewrite the operation
- op = block.operations[-1]
- op.opname = "lshift" # augmented later
- op.args = op.args[1:]
- elif is_single(block):
- # remove the call to ovfcheck and keep the exceptions
- check_syntax(block)
- remove_last_op(block)
- seen_ovfblocks[block] = True
- elif is_paired(block):
- # remove the block's exception links
- link = block.exits[0]
- ovfblock = link.target
- check_syntax(ovfblock, block)
- block.recloseblock(link)
- block.exitswitch = None
- # remove the ovfcheck call from the None target
- remove_last_op(ovfblock)
- seen_ovfblocks[ovfblock] = True
- blocks_to_join = True
- else:
- continue
- op = block.operations[-1]
- op.opname = "%s_%s" % (op.opname, appendix)
- if blocks_to_join:
- join_blocks(graph)
+ for block in graph.iterblocks():
+ for i in range(len(block.operations)-1, -1, -1):
+ op = block.operations[i]
+ if op.opname != 'simple_call':
+ continue
+ if op.args[0] == covf:
+ if i == 0:
+ # hard case: ovfcheck() on an operation that occurs
+ # in the previous block, like 'floordiv'. The generic
+ # exception handling around the ovfcheck() is enough
+ # to cover all cases; kill the one around the previous op.
+ entrymap = mkentrymap(graph)
+ links = entrymap[block]
+ assert len(links) == 1
+ prevblock = links[0].prevblock
+ assert prevblock.exits[0].target is block
+ prevblock.exitswitch = None
+ prevblock.exits = (links[0],)
+ join_blocks(graph) # merge the two blocks together
+ transform_ovfcheck(graph) # ...and try again
+ return
+ op1 = block.operations[i-1]
+ check_syntax(op1.opname)
+ op1.opname += '_ovf'
+ del block.operations[i]
+ block.renamevariables({op.result: op1.result})
+ elif op.args[0] == covfls:
+ op.opname = 'lshift_ovf'
+ del op.args[0]
def simplify_exceptions(graph):
"""The exception handling caused by non-implicit exceptions
Modified: pypy/branch/pyjitpl5/pypy/translator/test/test_simplify.py
==============================================================================
--- pypy/branch/pyjitpl5/pypy/translator/test/test_simplify.py (original)
+++ pypy/branch/pyjitpl5/pypy/translator/test/test_simplify.py Thu Mar 12 18:40:17 2009
@@ -15,6 +15,82 @@
t.view()
return graphof(t, func), t
+def test_remove_ovfcheck_1():
+ # check that ovfcheck() is handled
+ from pypy.rlib.rarithmetic import ovfcheck
+ def f(x):
+ try:
+ return ovfcheck(x*2)
+ except OverflowError:
+ return -42
+ graph, _ = translate(f, [int])
+ assert len(graph.startblock.operations) == 1
+ assert graph.startblock.operations[0].opname == 'int_mul_ovf'
+ assert len(graph.startblock.exits) == 2
+ assert [link.target.operations for link in graph.startblock.exits] == \
+ [(), ()]
+
+def test_remove_ovfcheck_bug():
+ # check that ovfcheck() is correctly handled even if there is no
+ # try:except: immediately around it
+ from pypy.rlib.rarithmetic import ovfcheck
+ def f(x):
+ return ovfcheck(x*2) - 1
+ graph, _ = translate(f, [int])
+ assert len(graph.startblock.operations) == 2
+ assert graph.startblock.operations[0].opname == 'int_mul_ovf'
+ assert graph.startblock.operations[1].opname == 'int_sub'
+
+def test_remove_ovfcheck_lshift():
+ # check that ovfcheck_lshift() is handled
+ from pypy.rlib.rarithmetic import ovfcheck_lshift
+ def f(x):
+ try:
+ return ovfcheck_lshift(x, 2)
+ except OverflowError:
+ return -42
+ graph, _ = translate(f, [int])
+ assert len(graph.startblock.operations) == 1
+ assert graph.startblock.operations[0].opname == 'int_lshift_ovf_val'
+ assert len(graph.startblock.operations[0].args) == 2
+ assert len(graph.startblock.exits) == 3
+ assert [link.target.operations for link in graph.startblock.exits] == \
+ [(), (), ()]
+
+def test_remove_ovfcheck_floordiv():
+ # check that ovfcheck() is handled even if the operation raises
+ # and catches another exception too, here ZeroDivisionError
+ from pypy.rlib.rarithmetic import ovfcheck
+ def f(x, y):
+ try:
+ return ovfcheck(x // y)
+ except OverflowError:
+ return -42
+ except ZeroDivisionError:
+ return -43
+ graph, _ = translate(f, [int, int])
+ assert len(graph.startblock.operations) == 1
+ assert graph.startblock.operations[0].opname == 'int_floordiv_ovf_zer'
+ assert len(graph.startblock.exits) == 3
+ assert [link.target.operations for link in graph.startblock.exits[1:]] == \
+ [(), ()]
+
+def test_remove_ovfcheck_floordiv_2():
+ # check that ovfcheck() is handled even if the operation raises
+ # and catches only another exception, here ZeroDivisionError
+ from pypy.rlib.rarithmetic import ovfcheck
+ def f(x, y):
+ try:
+ return ovfcheck(x // y)
+ except ZeroDivisionError:
+ return -43
+ graph, _ = translate(f, [int, int])
+ assert len(graph.startblock.operations) == 1
+ assert graph.startblock.operations[0].opname == 'int_floordiv_ovf_zer'
+ assert len(graph.startblock.exits) == 3
+ assert [link.target.operations for link in graph.startblock.exits[1:]] == \
+ [(), ()]
+
def test_remove_direct_call_without_side_effects():
def f(x):
return x + 123
More information about the Pypy-commit
mailing list