[pypy-svn] r25086 - in pypy/dist/pypy/translator/cli: . test

antocuni at codespeak.net antocuni at codespeak.net
Tue Mar 28 17:37:42 CEST 2006


Author: antocuni
Date: Tue Mar 28 17:37:30 2006
New Revision: 25086

Added:
   pypy/dist/pypy/translator/cli/test/test_overflow.py   (contents, props changed)
Modified:
   pypy/dist/pypy/translator/cli/cts.py
   pypy/dist/pypy/translator/cli/function.py
   pypy/dist/pypy/translator/cli/ilgenerator.py
   pypy/dist/pypy/translator/cli/opcodes.py
   pypy/dist/pypy/translator/cli/test/compile.py
Log:
Added support for exception handling in generated code.
Added initial support for overflow checking


Modified: pypy/dist/pypy/translator/cli/cts.py
==============================================================================
--- pypy/dist/pypy/translator/cli/cts.py	(original)
+++ pypy/dist/pypy/translator/cli/cts.py	Tue Mar 28 17:37:30 2006
@@ -2,8 +2,11 @@
 Translate between PyPy ootypesystem and .NET Common Type System
 """
 
+import exceptions
+
 from pypy.rpython.lltypesystem.lltype import Signed, Unsigned, Void, Bool, Float
 from pypy.rpython.lltypesystem.lltype import SignedLongLong, UnsignedLongLong
+from pypy.rpython.ootypesystem.ootype import Instance
 from pypy.translator.cli.option import getoption
 
 from pypy.tool.ansi_print import ansi_log
@@ -31,29 +34,29 @@
     'float64': 'r8',
     }
 
-def lltype_to_cts(t):
+def _get_from_dict(d, key, error):
     try:
-        return _lltype_to_cts[t]
+        return d[key]
     except KeyError:
         if getoption('nostop'):
-            log.WARNING('Unknown type %s' % t)
-            return t
+            log.WARNING(error)
+            return key
         else:
-            assert False, 'Unknown type %s' % t
+            assert False, error
+
+
+def lltype_to_cts(t):
+    # TODO: handle instances more accurately
+    if isinstance(t, Instance):
+        return 'object'
+
+    return _get_from_dict(_lltype_to_cts, t, 'Unknown type %s' % t)
 
 def lltype_to_ilasm(t):
     return ctstype_to_ilasm(lltype_to_cts(t))
 
 def ctstype_to_ilasm(t):
-    try:
-        return _cts_to_ilasm[t]
-    except KeyError:
-        if getoption('nostop'):
-            log.WARNING('Unknown ilasm type %s' % t)
-            return t
-        else:
-            assert False, 'Unknown ilasm type %s' % t
-
+    return _get_from_dict(_cts_to_ilasm, t, 'Unknown ilasm type %s' % t)
 
 def llvar_to_cts(var):
     return lltype_to_cts(var.concretetype), var.name
@@ -80,3 +83,11 @@
     arg_list = ', '.join(arg_types)
 
     return '%s %s(%s)' % (ret_type, func_name, arg_list)
+
+_pyexception_to_cts = {
+    exceptions.Exception: '[mscorlib]System.Exception',
+    exceptions.OverflowError: '[mscorlib]System.OverflowException'
+    }
+
+def pyexception_to_cts(exc):
+    return _get_from_dict(_pyexception_to_cts, exc, 'Unknown exception %s' % exc)

Modified: pypy/dist/pypy/translator/cli/function.py
==============================================================================
--- pypy/dist/pypy/translator/cli/function.py	(original)
+++ pypy/dist/pypy/translator/cli/function.py	Tue Mar 28 17:37:30 2006
@@ -23,50 +23,93 @@
         self.graph = graph
         self.is_entrypoint = is_entrypoint
         self.blocknum = {}
-
         self._set_args()
         self._set_locals()
 
     def get_name(self):
         return self.graph.name
 
+    def _is_return_block(self, block):
+        return (not block.exits) and len(block.inputargs) == 1
+
+    def _is_raise_block(self, block):
+        return (not block.exits) and len(block.inputargs) == 2        
+
     def render(self, ilasm):
         self.ilasm = ilasm
         graph = self.graph
         returntype, returnvar = cts.llvar_to_cts(graph.getreturnvar())
         
-        ilasm.begin_function(graph.name, self.args, returntype, self.is_entrypoint)
-        ilasm.locals(self.locals)
+        self.ilasm.begin_function(graph.name, self.args, returntype, self.is_entrypoint)
+        self.ilasm.locals(self.locals)
 
         for block in graph.iterblocks():
-            ilasm.label(self._get_block_name(block))
+            self.ilasm.label(self._get_block_name(block))
+
+            handle_exc = (block.exitswitch == flowmodel.c_last_exception)
+            if handle_exc:
+                self.ilasm.begin_try()
 
             for op in block.operations:
                 self._render_op(op)
 
-            # if it is the last block, return the result
-            if not block.exits:
-                assert len(block.inputargs) == 1
+            # check for codeless blocks
+            if self._is_return_block(block):
                 return_var = block.inputargs[0]
                 if return_var.concretetype is not Void:
                     self._push(return_var)
-                ilasm.opcode('ret')
+                self.ilasm.opcode('ret')
+            elif self._is_raise_block(block):
+                exc = block.inputargs[1]
+                self._push(exc)
+                self.ilasm.opcode('throw')
+
+            if handle_exc:
+                # search for the "default" block to be executed when no exception is raised
+                for link in block.exits:
+                    if link.exitcase is None:
+                        self._setup_link(link)
+                        target_label = self._get_block_name(link.target)
+                        self.ilasm.leave(target_label)
+                self.ilasm.end_try()
+
+                # catch the exception and dispatch to the appropriate block
+                for link in block.exits:
+                    if link.exitcase is None:
+                        continue # see above
+
+                    assert issubclass(link.exitcase, Exception)
+                    cts_exc = cts.pyexception_to_cts(link.exitcase)
+                    self.ilasm.begin_catch(cts_exc)
+
+                    target = link.target
+                    if self._is_raise_block(target):
+                        # the exception value is on the stack, use it as the 2nd target arg
+                        assert len(link.args) == 2
+                        assert len(target.inputargs) == 2
+                        self._store(link.target.inputargs[1])
+                    else:
+                        # pop the unused exception value
+                        self.ilasm.opcode('pop')
+                        self._setup_link(link)
+                    
+                    target_label = self._get_block_name(target)
+                    self.ilasm.leave(target_label)
+                    self.ilasm.end_catch()
+
+            else:
+                # no exception handling, follow block links
+                for link in block.exits:
+                    self._setup_link(link)
+                    target_label = self._get_block_name(link.target)
+                    if link.exitcase is None:
+                        self.ilasm.branch(target_label)
+                    else:
+                        assert type(link.exitcase is bool)
+                        assert block.exitswitch is not None
+                        self._push(block.exitswitch)
+                        self.ilasm.branch_if(link.exitcase, target_label)
 
-            # follow block links
-            for link in block.exits:
-                target = link.target
-                for to_load, to_store in zip(link.args, target.inputargs):
-                    if to_load.concretetype is not Void:
-                        self._push(to_load)
-                        self._store(to_store)
-
-                target_label = self._get_block_name(target)
-                if link.exitcase is None:
-                    self.ilasm.branch(target_label)
-                else:
-                    assert type(link.exitcase is bool)
-                    self._push(block.exitswitch)
-                    self.ilasm.branch_if(link.exitcase, target_label)
 
         # add a block that will never be executed, just to please the
         # .NET runtime that seems to need a return statement at the
@@ -76,8 +119,15 @@
             self.ilasm.opcode('ldc.%s 0' % ilasm_type)
 
         self.ilasm.opcode('ret')
-        
-        ilasm.end_function()
+        self.ilasm.end_function()
+
+    def _setup_link(self, link):
+        target = link.target
+        for to_load, to_store in zip(link.args, target.inputargs):
+            if to_load.concretetype is not Void:
+                self._push(to_load)
+                self._store(to_store)
+
 
     def _set_locals(self):
         # this code is partly borrowed from pypy.translator.c.funcgen.FunctionCodeGenerator
@@ -187,6 +237,7 @@
 
     def _store(self, v):
         if isinstance(v, flowmodel.Variable):
-            self.ilasm.opcode('stloc', repr(v.name))
+            if v.concretetype is not Void:
+                self.ilasm.opcode('stloc', repr(v.name))
         else:
             assert False

Modified: pypy/dist/pypy/translator/cli/ilgenerator.py
==============================================================================
--- pypy/dist/pypy/translator/cli/ilgenerator.py	(original)
+++ pypy/dist/pypy/translator/cli/ilgenerator.py	Tue Mar 28 17:37:30 2006
@@ -56,6 +56,20 @@
     def end_function(self):
         self.code.closeblock()
 
+    def begin_try(self):
+        self.code.writeline('.try')
+        self.code.openblock()
+
+    def end_try(self):
+        self.code.closeblock()
+
+    def begin_catch(self, type_):
+        self.code.writeline('catch ' + type_)
+        self.code.openblock()
+
+    def end_catch(self):
+        self.code.closeblock()
+
     def locals(self, vars):
         varlist = ', '.join(['%s %s' % var for var in vars])        
         self.code.write('.locals init (')
@@ -67,6 +81,9 @@
         self.code.write(lbl + ':', indent=0)
         self.code.writeline()
 
+    def leave(self, lbl):
+        self.code.writeline('leave ' + lbl)
+
     def branch(self, lbl):
         self.code.writeline('br ' + lbl)
 

Modified: pypy/dist/pypy/translator/cli/opcodes.py
==============================================================================
--- pypy/dist/pypy/translator/cli/opcodes.py	(original)
+++ pypy/dist/pypy/translator/cli/opcodes.py	Tue Mar 28 17:37:30 2006
@@ -51,11 +51,10 @@
     'int_lshift':               'shl',
     'int_rshift':               'shr',
     'int_xor':                  'xor',
-    'int_add_ovf':              None,
-    'int_sub_ovf':              None,
-    'int_mul_ovf':              None,
-    'int_div_ovf':              None,
-    'int_truediv_ovf':          None,
+    'int_add_ovf':              'add.ovf',
+    'int_sub_ovf':              'sub.ovf',
+    'int_mul_ovf':              'mul.ovf',
+#    'int_div_ovf':              None,
     'int_floordiv_ovf':         None,
     'int_mod_ovf':              None,
     'int_lt_ovf':               None,

Modified: pypy/dist/pypy/translator/cli/test/compile.py
==============================================================================
--- pypy/dist/pypy/translator/cli/test/compile.py	(original)
+++ pypy/dist/pypy/translator/cli/test/compile.py	Tue Mar 28 17:37:30 2006
@@ -24,37 +24,23 @@
         print 'OK'
 
 
-def foo(x):
-    pass
-
-def xxx():
+def foo():
     pass
 
 def bar(x, y):
     try:
-        foo(x)
-        z = ovfcheck(x+y)
-        xxx()
-        return z
+        foo()
+        z = x
     except OverflowError:
-        while x:
-            x = x-1
-        return x
-    except IndexError:
-        return 52
-            
+        z = x+y
+
+    return z
 
-def bar(x, y):
-    foo(x)
-    foo(None)
 
 f = compile_function(bar, [int, int])
 
 try:
-    check(f, bar, r_uint(sys.maxint+1), r_uint(42))
-    check(f, bar, 4, 5)    
+    pass
 except py.test.Item.Skipped:
     print 'Test skipped'
 
-
-#compile_function(s.is_perfect_number, [int])

Added: pypy/dist/pypy/translator/cli/test/test_overflow.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/translator/cli/test/test_overflow.py	Tue Mar 28 17:37:30 2006
@@ -0,0 +1,27 @@
+from pypy.translator.cli.test.runtest import check
+from pypy.rpython.rarithmetic import ovfcheck
+
+import sys
+
+def op_add(x, y):
+    try:
+        return ovfcheck(x+y)
+    except OverflowError:
+        return 42
+
+def op_sub(x, y):
+    try:
+        return ovfcheck(x-y)
+    except OverflowError:
+        return 42
+
+def op_mul(x, y):
+    try:
+        return ovfcheck(x*y)
+    except OverflowError:
+        return 42
+
+def test_overflow():
+    yield check, op_add, [int, int], (sys.maxint, 1)
+    yield check, op_sub, [int, int], (-sys.maxint, 1)
+    yield check, op_mul, [int, int], (sys.maxint/2 + 1, 2)



More information about the Pypy-commit mailing list