[pypy-svn] r59035 - in pypy/dist/pypy/translator/backendopt: . test

arigo at codespeak.net arigo at codespeak.net
Sun Oct 12 17:02:30 CEST 2008


Author: arigo
Date: Sun Oct 12 17:02:30 2008
New Revision: 59035

Modified:
   pypy/dist/pypy/translator/backendopt/mallocv.py
   pypy/dist/pypy/translator/backendopt/test/test_mallocv.py
Log:
(pedronis for a bit, arigo)
Progress.  One more test passes.  That took the whole afternoon :-/


Modified: pypy/dist/pypy/translator/backendopt/mallocv.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/mallocv.py	(original)
+++ pypy/dist/pypy/translator/backendopt/mallocv.py	Sun Oct 12 17:02:30 2008
@@ -4,6 +4,7 @@
 from pypy.translator.backendopt.support import log
 from pypy.rpython.typesystem import getfunctionptr
 from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.lltypesystem.lloperation import llop
 
 
 # ____________________________________________________________
@@ -163,37 +164,6 @@
         rtnodes, vtnodes = self.find_all_nodes()
         return vtnodes
 
-    def return_to_caller(self, retnode):
-        callerframe = self.callerframe
-        if callerframe is None:
-            raise ForcedInline("return block")
-        for i in range(len(callerframe.nodelist)):
-            if isinstance(callerframe.nodelist[i], FutureReturnValue):
-                callerframe.nodelist[i] = retnode
-        return callerframe
-
-    def handle_raise(self, linkargsnodes):
-        if not is_trivial_nodelist(linkargsnodes):
-            raise CannotVirtualize("except block")
-            # ^^^ this could also be a ForcedInline, to try to match the
-            # exception raising and catching globally.  But it looks
-            # overkill for now.
-
-        # XXX this assumes no exception handler in the callerframes
-        topframe = self
-        while topframe.callerframe is not None:
-            topframe = topframe.callerframe
-        targetblock = topframe.sourcegraph.exceptblock
-        self.fixup_except_block(targetblock)
-        return topframe, targetblock
-
-    def fixup_except_block(self, block):
-        # hack: this block's inputargs may be missing concretetypes...
-        e1, v1 = block.inputargs
-        e2, v2 = self.sourcegraph.exceptblock.inputargs
-        e1.concretetype = e2.concretetype
-        v1.concretetype = v2.concretetype
-
 
 def copynodes(nodelist, flagreadonly={}):
     memo = {}
@@ -225,8 +195,10 @@
 
 class MallocVirtualizer(object):
 
-    def __init__(self, graphs, verbose=False):
+    def __init__(self, graphs, rtyper, verbose=False):
         self.graphs = graphs
+        self.rtyper = rtyper
+        self.excdata = rtyper.getexceptiondata()
         self.graphbuilders = {}
         self.specialized_graphs = {}
         self.inline_and_remove = {}    # {graph: op_to_remove}
@@ -279,6 +251,12 @@
             if mode == 'fail':
                 del self.specialized_graphs[key]
 
+    def fixup_except_block(self, exceptblock):
+        # hack: this block's inputargs may be missing concretetypes...
+        e1, v1 = exceptblock.inputargs
+        e1.concretetype = self.excdata.lltype_of_exception_type
+        v1.concretetype = self.excdata.lltype_of_exception_value
+
     def getmalloctypedesc(self, MALLOCTYPE):
         try:
             dsc = self.malloctypedescs[MALLOCTYPE]
@@ -399,6 +377,93 @@
         srcblock.exitswitch = specblock.exitswitch
         srcblock.recloseblock(*specblock.exits)
 
+    def create_outgoing_link(self, currentframe, targetblock,
+                             nodelist, renamings, v_expand_malloc=None):
+        assert len(nodelist) == len(targetblock.inputargs)
+        #
+        if is_except(targetblock):
+            v_expand_malloc = None
+            v_exc_type = renamings.get(nodelist[0])
+            while currentframe.callerframe is not None:
+                currentframe = currentframe.callerframe
+                newlink = self.handle_catch(currentframe, v_exc_type,
+                                            renamings)
+                if newlink:
+                    return newlink
+            else:
+                targetblock = self.exception_escapes(nodelist)
+                assert len(nodelist) == len(targetblock.inputargs)
+
+        if (currentframe.callerframe is None and
+              is_trivial_nodelist(nodelist)):
+            # there is no more VirtualSpecNodes being passed around,
+            # so we can stop specializing
+            rtnodes = nodelist
+            specblock = targetblock
+        else:
+            if is_return(targetblock):
+                v_expand_malloc = None
+                newframe = self.return_to_caller(currentframe, nodelist[0])
+            else:
+                targetnodes = dict(zip(targetblock.inputargs, nodelist))
+                newframe = VirtualFrame(currentframe.sourcegraph,
+                                        targetblock, 0, targetnodes,
+                                        callerframe=currentframe.callerframe)
+            rtnodes = newframe.find_rt_nodes()
+            specblock = self.get_specialized_block(newframe, v_expand_malloc)
+
+        linkargs = [renamings[rtnode] for rtnode in rtnodes]
+        return Link(linkargs, specblock)
+
+    def return_to_caller(self, currentframe, retnode):
+        callerframe = currentframe.callerframe
+        if callerframe is None:
+            raise ForcedInline("return block")
+        for i in range(len(callerframe.nodelist)):
+            if isinstance(callerframe.nodelist[i], FutureReturnValue):
+                callerframe.nodelist[i] = retnode
+        return callerframe
+
+    def handle_catch(self, catchingframe, v_exc_type, renamings):
+        if not self.has_exception_catching(catchingframe):
+            return None
+        if not isinstance(v_exc_type, Constant):
+            raise CannotVirtualize("raising non-constant exc type")
+        excdata = self.mallocv.excdata
+        assert catchingframe.sourceblock.exits[0].exitcase is None
+        for catchlink in catchingframe.sourceblock.exits[1:]:
+            if excdata.fn_exception_match(v_exc_type.value,
+                                          catchlink.llexitcase):
+                # Match found.  Follow this link.
+                mynodes = catchingframe.get_nodes_in_use()
+                # XXX Constants
+                nodelist = [mynodes[v] for v in catchlink.args]
+                return self.create_outgoing_link(catchingframe,
+                                                 catchlink.target,
+                                                 nodelist, renamings)
+        else:
+            # No match at all, propagate the exception to the caller
+            return None
+
+    def has_exception_catching(self, catchingframe):
+        if catchingframe.sourceblock.exitswitch != c_last_exception:
+            return False
+        else:
+            operations = catchingframe.sourceblock.operations
+            assert 1 <= catchingframe.nextopindex <= len(operations)
+            return catchingframe.nextopindex == len(operations)
+
+    def exception_escapes(self, nodelist):
+        # the exception escapes
+        if not is_trivial_nodelist(nodelist):
+            raise CannotVirtualize("except block")
+            # ^^^ this could also be a ForcedInline, to try to match the
+            # exception raising and catching globally.  But it looks
+            # overkill for now.
+        targetblock = self.graph.exceptblock
+        self.mallocv.fixup_except_block(targetblock)
+        return targetblock
+
     def get_specialized_block(self, virtualframe, v_expand_malloc=None):
         key = virtualframe.getfrozenkey()
         specblock = self.specialized_blocks.get(key)
@@ -448,7 +513,7 @@
         if isinstance(v, Variable):
             return self.nodes[v]
         else:
-            rtnode = RuntimeSpecNode('const', v.concretetype)
+            rtnode = RuntimeSpecNode(None, v.concretetype)
             self.renamings[rtnode] = v
             return rtnode
 
@@ -466,6 +531,7 @@
 
     def specialize_operations(self):
         newoperations = []
+        self.ops_produced_by_last_op = 0
         # note that 'self.virtualframe' can be changed during the loop!
         while True:
             operations = self.virtualframe.sourceblock.operations
@@ -477,18 +543,30 @@
 
             meth = getattr(self, 'handle_op_' + op.opname,
                            self.handle_default)
-            newoperations += meth(op)
+            newops_for_this_op = meth(op)
+            newoperations += newops_for_this_op
+            self.ops_produced_by_last_op = len(newops_for_this_op)
         self.specblock.operations = newoperations
 
     def follow_exits(self):
         block = self.virtualframe.sourceblock
         self.specblock.exitswitch = self.rename_nonvirtual(block.exitswitch,
                                                            'exitswitch')
+        links = block.exits
         catch_exc = self.specblock.exitswitch == c_last_exception
+        if catch_exc and self.ops_produced_by_last_op == 0:
+            # the last op of the sourceblock did not produce any
+            # operation in specblock, so we need to discard the
+            # exception-catching.
+            catch_exc = False
+            links = links[:1]
+            assert links[0].exitcase is None  # the non-exception-catching case
+            self.specblock.exitswitch = None
+
         newlinks = []
-        for link in block.exits:
-            is_exc_link = catch_exc and link.exitcase is not None
-            if is_exc_link:
+        for link in links:
+            is_catch_link = catch_exc and link.exitcase is not None
+            if is_catch_link:
                 extravars = []
                 for attr in ['last_exception', 'last_exc_value']:
                     v = getattr(link, attr)
@@ -498,44 +576,19 @@
                         self.renamings[rtnode] = v = rtnode.newvar()
                     extravars.append(v)
 
-            currentframe = self.virtualframe
             linkargsnodes = [self.getnode(v1) for v1 in link.args]
-            targetblock = link.target
-
-            if is_except(targetblock):
-                currentframe, targetblock = currentframe.handle_raise(
-                    linkargsnodes)
-
-            assert len(targetblock.inputargs) == len(linkargsnodes)
-            targetnodes = dict(zip(targetblock.inputargs, linkargsnodes))
-
-            if (currentframe.callerframe is None and
-                  is_trivial_nodelist(linkargsnodes)):
-                # there is no more VirtualSpecNodes being passed around,
-                # so we can stop specializing
-                rtnodes = linkargsnodes
-                specblock = targetblock
-            else:
-                if is_return(targetblock):
-                    newframe = currentframe.return_to_caller(linkargsnodes[0])
-                    v_expand_malloc = None
-                else:
-                    newframe = VirtualFrame(currentframe.sourcegraph,
-                                            targetblock, 0, targetnodes,
-                                          callerframe=currentframe.callerframe)
-                    v_expand_malloc = self.v_expand_malloc
-                rtnodes = newframe.find_rt_nodes()
-                specblock = self.graphbuilder.get_specialized_block(newframe,
-                                                               v_expand_malloc)
-
-            linkargs = [self.renamings[rtnode] for rtnode in rtnodes]
-            newlink = Link(linkargs, specblock)
+            #
+            newlink = self.graphbuilder.create_outgoing_link(
+                self.virtualframe, link.target, linkargsnodes,
+                self.renamings, self.v_expand_malloc)
+            #
             newlink.exitcase = link.exitcase
             if hasattr(link, 'llexitcase'):
                 newlink.llexitcase = link.llexitcase
-            if is_exc_link:
+            if is_catch_link:
                 newlink.extravars(*extravars)
             newlinks.append(newlink)
+
         self.specblock.closeblock(*newlinks)
 
     def make_rt_result(self, v_result):
@@ -545,10 +598,25 @@
         self.renamings[newrtnode] = v_new
         return v_new
 
+    def make_const_rt_result(self, v_result, value):
+        newrtnode = RuntimeSpecNode(v_result, v_result.concretetype)
+        self.setnode(v_result, newrtnode)
+        if v_result.concretetype is not lltype.Void:
+            assert v_result.concretetype == lltype.typeOf(value)
+        c_value = Constant(value)
+        c_value.concretetype = v_result.concretetype
+        self.renamings[newrtnode] = c_value
+
     def handle_default(self, op):
         newargs = [self.rename_nonvirtual(v, op) for v in op.args]
-        newresult = self.make_rt_result(op.result)
-        return [SpaceOperation(op.opname, newargs, newresult)]
+        constresult = try_fold_operation(op.opname, newargs,
+                                         op.result.concretetype)
+        if constresult:
+            self.make_const_rt_result(op.result, constresult[0])
+            return []
+        else:
+            newresult = self.make_rt_result(op.result)
+            return [SpaceOperation(op.opname, newargs, newresult)]
 
     def handle_unreachable(self, op):
         from pypy.rpython.lltypesystem.rstr import string_repr
@@ -735,3 +803,30 @@
 
 def is_except(block):
     return len(block.exits) == 0 and len(block.inputargs) == 2
+
+class CannotConstFold(Exception):
+    pass
+
+def try_fold_operation(opname, args_v, RESTYPE):
+    args = []
+    for c in args_v:
+        if not isinstance(c, Constant):
+            return
+        args.append(c.value)
+    try:
+        op = getattr(llop, opname)
+    except AttributeError:
+        return
+    if not op.is_pure(*[v.concretetype for v in args_v]):
+        return
+    try:
+        result = op(RESTYPE, *args)
+    except TypeError:
+        pass
+    except (KeyboardInterrupt, SystemExit):
+        raise
+    except Exception, e:
+        log.WARNING('constant-folding %s%r:' % (opname, args_v))
+        log.WARNING('  %s: %s' % (e.__class__.__name__, e))
+    else:
+        return (result,)

Modified: pypy/dist/pypy/translator/backendopt/test/test_mallocv.py
==============================================================================
--- pypy/dist/pypy/translator/backendopt/test/test_mallocv.py	(original)
+++ pypy/dist/pypy/translator/backendopt/test/test_mallocv.py	Sun Oct 12 17:02:30 2008
@@ -54,7 +54,7 @@
         # to detect missing keepalives and broken intermediate graphs,
         # we do the loop ourselves instead of calling remove_simple_mallocs()
         maxiter = 100
-        mallocv = MallocVirtualizer(t.graphs, verbose=True)
+        mallocv = MallocVirtualizer(t.graphs, t.rtyper, verbose=True)
         while True:
             progress = mallocv.remove_mallocs_once()
             #simplify.transform_dead_op_vars_in_blocks(list(graph.iterblocks()))
@@ -273,7 +273,7 @@
         self.check(f, [int], [-19], CHECK_RAISES("ValueError"))
 
     def test_call_raise_catch(self):
-        py.test.skip("in-progress")
+        #py.test.skip("in-progress")
         class A:
             pass
         def g(a):



More information about the Pypy-commit mailing list