[pypy-svn] r30987 - pypy/dist/pypy/translator/stackless

mwh at codespeak.net mwh at codespeak.net
Fri Aug 4 13:47:18 CEST 2006


Author: mwh
Date: Fri Aug  4 13:47:17 2006
New Revision: 30987

Modified:
   pypy/dist/pypy/translator/stackless/transform.py
Log:
one more try to reuse some of the resume block code.
reduces the size of pypy-c-stackess by another 100k or so -- not sure that's
worth all the effort really, but the code is a bit cleaner now too.


Modified: pypy/dist/pypy/translator/stackless/transform.py
==============================================================================
--- pypy/dist/pypy/translator/stackless/transform.py	(original)
+++ pypy/dist/pypy/translator/stackless/transform.py	Fri Aug  4 13:47:17 2006
@@ -4,7 +4,7 @@
 from pypy.translator.backendopt import support
 from pypy.objspace.flow import model
 from pypy.rpython.memory.gctransform import varoftype
-from pypy.translator import unsimplify
+from pypy.translator import unsimplify, simplify
 from pypy.annotation import model as annmodel
 from pypy.rpython.annlowlevel import MixLevelHelperAnnotator
 from pypy.translator.stackless import code, frame
@@ -22,6 +22,28 @@
 
 SAVE_STATISTICS = True
 
+# we do this _a lot_:
+def copyvar(var):
+    if isinstance(var, model.Variable):
+        return unsimplify.copyvar(None, var)
+    else:
+        return varoftype(var.concretetype)
+
+def copy_link_with_varmap(link, varmap):
+    varmap = varmap.copy()
+    def rename(arg):
+        if isinstance(arg, model.Variable):
+            if arg in varmap:
+                return varmap[arg]
+            else:
+                assert arg in [link.last_exception, link.last_exc_value]
+                r = copyvar(arg)
+                varmap[arg] = r
+                return r
+        else:
+            return arg
+    return link.copy(rename)
+
 if SAVE_STATISTICS:
     import cStringIO
     
@@ -36,6 +58,9 @@
             self.total_pot_exact_saves = 0
             self.pot_erased_saves = {}
             self.total_pot_erased_saves = 0
+            self.saved_retrieval_ops = 0
+            self.saved_cast_ops = 0
+            self.saved_return_ops = 0
         def __repr__(self):
             s = cStringIO.StringIO()
             print >> s, self.__class__.__name__
@@ -101,6 +126,32 @@
         # complete.
         return self.value
 
+# the strategy for sharing parts of the resume code:
+#
+# when a function is being resumed, there are three things that need to
+# be done: the values (as erased types) need to be read out of the
+# frame state structure, the return value needs to be read out of
+# the global_state (this is also when the check is made if we are
+# resuming with an exception) and the return value might need to be
+# cast to an exact type.  our strategy is to do each of these things
+# in separate blocks, reusing blocks where we can.  the retrieval and
+# cast blocks will (conceptually at least) have a switch on the
+# restate subcase at the end of it.
+#
+# conditions for reuse:
+#
+# 1. retrieval block: the erased types being read out
+#
+# 2. the return value blocks: the erased the erased return type and the exception
+#    and target of any exception links
+#
+# 3. the cast blocks: the exact types required.
+#
+# note that we order types by the index of the erased type in
+# STORAGE_TYPES, to increase the chance that we can resuse the types.
+
+
+
 class FrameTyper:
     # this class only exists independently to ease testing
     def __init__(self, stackless_gc=False, transformer=None):
@@ -449,10 +500,13 @@
         assert self.curr_graph is None
         self.curr_graph = graph
         self.curr_graph_save_blocks = {}
+        self.curr_graph_resume_retrieval_blocks = {}
+        self.curr_graph_resume_return_blocks = {}
+        self.curr_graph_resume_cast_blocks = {}
+        
         if SAVE_STATISTICS:
             self.stats.cur_rp_exact_types = {}
             self.stats.cur_rp_erased_types = {}
-            
         
         for block in list(graph.iterblocks()):
             assert block not in self.seen_blocks
@@ -520,7 +574,7 @@
         resuming_links = []
         for resume_index, resume_block in enumerate(self.resume_blocks):
             resuming_links.append(
-                model.Link([], resume_block, resume_index))
+                model.Link([var_resume_state], resume_block, resume_index))
             resuming_links[-1].llexitcase = resume_index
 
         new_start_block.exitswitch = var_resume_state
@@ -530,6 +584,14 @@
         new_start_block.isstartblock = True
         graph.startblock = new_start_block
 
+        for block in graph.iterblocks():
+            if len(block.exits) == 1 and block.exitswitch is not None:
+                block.exitswitch = None
+                block.exits[0].exitcase = block.exits[0].llexitcase = None
+        simplify.simplify_graph(graph, [simplify.eliminate_empty_blocks,
+                                        simplify.join_blocks,
+                                        simplify.transform_dead_op_vars])
+
     def insert_return_conversion(self, link, targettype, retvar):
         llops = LowLevelOpList()
         newvar = gen_cast(llops, targettype, retvar)
@@ -584,10 +646,9 @@
         else:
             self.explicit_resume_point_data[label] = frame_type
 
-        self.resume_blocks.append(
-            self._generate_resume_block(varsforcall, frame_type, res, block.exits))
+        self._make_resume_handling(frame_type, varsforcall, res, block.exits)
 
-        restart_number = len(self.masterarray1) + len(self.resume_blocks)-1
+        restart_number = len(self.masterarray1) + len(self.resume_blocks) - 1
 
         if label in self.symbolic_restart_numbers:
             symb = self.symbolic_restart_numbers[label]
@@ -745,11 +806,9 @@
 
         args = vars_to_save(block)
 
-        save_block, resume_block, varsforcall = self.generate_save_and_resume_blocks(
+        save_block, varsforcall = self.generate_save_and_resume_blocks(
             args, var_unwind_exception, op.result, block.exits)
 
-        self.resume_blocks.append(resume_block)
-
         newlink = model.Link(varsforcall + [var_unwind_exception], 
                              save_block, code.UnwindException)
         newlink.last_exception = model.Constant(code.UnwindException,
@@ -843,9 +902,10 @@
         varsforcall.insert(0, c_restart)
         varsforcall = [v for v in varsforcall if v.concretetype != lltype.Void]
         
+        self._make_resume_handling(frame_type, varsforcall0,
+                                   var_result, links_to_resumption)
+        
         return (self._generate_save_block(varsforcall, var_exception, saver),
-                self._generate_resume_block(varsforcall0, frame_type,
-                                            var_result, links_to_resumption),
                 varsforcall)
 
     def _generate_save_block(self, varsforcall, var_unwind_exception, saver):
@@ -960,6 +1020,182 @@
         if SAVE_STATISTICS:
             self.stats.resumeops += len(newblock.operations)
         return newblock
+
+    def _make_resume_handling(self, FRAME_TYPE, sorted_vars, v_retval, links_to_resumption):
+        resume_substate = len(self.resume_blocks)
+        
+        erased_types = []
+        for v in sorted_vars:
+            if v.concretetype != lltype.Void:
+                erased_types.append(storage_type(v.concretetype))
+
+        retval_type = v_retval.concretetype
+        erased_retval_type = storage_type(retval_type)
+
+        retrieve_block, output_args = self._get_resume_retrieval_block(FRAME_TYPE, erased_types)
+
+        return_block, switch_block = self._get_resume_return_block(
+            erased_types, erased_retval_type, links_to_resumption[1:], sorted_vars)
+
+        link = model.Link(output_args, return_block, resume_substate)
+        link.llexitcase = link.exitcase
+        retrieve_block.recloseblock(*(tuple(retrieve_block.exits) + (link,)))
+
+        if erased_retval_type != lltype.Void:
+            erased_types.append(erased_retval_type)
+        cast_block, cast_args = self._get_resume_cast_block(
+            erased_types,
+            [v.concretetype for v in sorted_vars] + [retval_type])
+
+        link = model.Link(switch_block.inputargs, cast_block, resume_substate)
+        link.llexitcase = resume_substate
+        switch_block.recloseblock(*(tuple(switch_block.exits) + (link,)))
+        
+        varmap = dict([(v, cast_args[sorted_vars.index(v)]) for v in sorted_vars])
+        for k, v in varmap.items():
+            assert k.concretetype == v.concretetype
+        
+        varmap[v_retval] = cast_args[-1]
+        
+        link = copy_link_with_varmap(links_to_resumption[0], varmap)
+        link.exitcase = link.llexitcase = resume_substate
+        cast_block.recloseblock(*(tuple(cast_block.exits) + (link,)))
+
+        self.resume_blocks.append(retrieve_block)
+
+    def _make_resume_retrieval_block(self, FRAME_TYPE, erased_types):
+        retrieve_block = model.Block([varoftype(lltype.Signed)])
+        retrieve_block.exitswitch = retrieve_block.inputargs[0]
+
+        llops = LowLevelOpList()
+        llops.genop("setfield",
+                    [self.ll_global_state, self.c_restart_substate_name, self.c_minus_one])
+        v_state_hdr = llops.genop("getfield",
+                                  [self.ll_global_state, self.c_inst_top_name],
+                                  resulttype=lltype.Ptr(STATE_HEADER))
+        v_state = gen_cast(llops, lltype.Ptr(FRAME_TYPE), v_state_hdr)
+        llops.genop("setfield",
+                    [self.ll_global_state, self.c_inst_top_name, self.c_null_state])
+        output_args = [retrieve_block.inputargs[0]]
+        assert len(FRAME_TYPE._names[1:]) == len(erased_types)
+        for fieldname, typ in zip(FRAME_TYPE._names[1:], erased_types):
+            assert FRAME_TYPE._flds[fieldname] == typ
+            output_args.append(llops.genop("getfield",
+                                           [v_state, model.Constant(fieldname, lltype.Void)],
+                                           resulttype=typ))
+        retrieve_block.operations = llops
+        return retrieve_block, output_args
+
+    def _get_resume_retrieval_block(self, FRAME_TYPE, erased_types):
+        key = tuple(erased_types)
+        if key in self.curr_graph_resume_retrieval_blocks:
+            retrieve_block, output_args = self.curr_graph_resume_retrieval_blocks[key]
+            if SAVE_STATISTICS:
+                self.stats.saved_retrieval_ops += len(retrieve_block.operations)
+            return retrieve_block, output_args
+        else:
+            retrieve_block, output_args = self._make_resume_retrieval_block(
+                FRAME_TYPE, erased_types)
+            self.curr_graph_resume_retrieval_blocks[key] = retrieve_block, output_args
+            return retrieve_block, output_args
+
+    def _make_resume_return_block(self, erased_types, erased_retval_type, except_links, sorted_vars):
+        inputargs = [varoftype(lltype.Signed)] + [varoftype(t) for t in erased_types]
+        return_block = model.Block(inputargs)
+        return_block.exitswitch = model.c_last_exception
+        llops = LowLevelOpList()
+        
+        getretval = self.fetch_retvals[erased_retval_type]        
+        v_retval = llops.genop("direct_call", [getretval],
+                               resulttype=erased_retval_type)
+
+        switch_block = model.Block([copyvar(v) for v in inputargs])
+        switch_block.exitswitch = switch_block.inputargs[0]
+
+        retlink = model.Link(inputargs, switch_block, None)
+        
+        if erased_retval_type != lltype.Void:
+            retlink.args.append(v_retval)
+            switch_block.inputargs.append(copyvar(v_retval))
+
+        links = [retlink]
+
+        for except_link in except_links:
+            cast_block, cast_args = self._make_cast_block(
+                erased_types, [v.concretetype for v in sorted_vars])
+            varmap = dict([(v, cast_args[sorted_vars.index(v)]) for v in sorted_vars])
+            
+            link = model.Link(inputargs[1:], cast_block, except_link.exitcase)
+            link.llexitcase = except_link.llexitcase
+            for attr in "last_exception", "last_exc_value":
+                old = getattr(except_link, attr)
+                new = copyvar(old)
+                setattr(link, attr, new)
+                link.args.append(new)
+                newnew = copyvar(new)
+                cast_block.inputargs.append(newnew)
+                varmap[old] = newnew
+            links.append(link)
+
+            link = copy_link_with_varmap(except_link, varmap)
+            link.exitcase = link.llexitcase = None
+            link.last_exception = link.last_exc_value = None
+            cast_block.closeblock(link)
+
+        return_block.operations = llops
+        return_block.closeblock(*links)
+
+        return return_block, switch_block
+
+    def _get_resume_return_block(self, erased_types, erased_retval_type, except_links, sorted_vars):
+        key = (erased_retval_type,)
+        key += tuple(erased_types)
+        key += tuple([(elink.exitcase, elink.target) for elink in except_links])
+        if except_links and max([len(elink.args) for elink in except_links]) > 2:
+            key = None
+        if key in self.curr_graph_resume_return_blocks:
+            return_block, switch_block = self.curr_graph_resume_return_blocks[key]
+            if SAVE_STATISTICS:
+                self.stats.saved_return_ops += len(return_block.operations)
+            return return_block, switch_block
+        else:
+            return_block, switch_block = self._make_resume_return_block(erased_types, erased_retval_type, except_links, sorted_vars)
+            if key is not None:
+                self.curr_graph_resume_return_blocks[key] = return_block, switch_block
+            return return_block, switch_block
+
+    def _make_cast_block(self, erased_types, exact_types):
+        inputargs = [varoftype(t) for t in erased_types]
+        cast_block = model.Block(inputargs)
+        cast_block.operations = LowLevelOpList()
+        output_args = []
+        assert len(inputargs) == len([typ for typ in exact_types if typ != lltype.Void])
+        i_arg = 0
+        for typ in exact_types:
+            if typ == lltype.Void:
+                output_args.append(model.Constant(None, lltype.Void))
+            else:
+                arg = inputargs[i_arg]
+                i_arg += 1
+                output_args.append(gen_cast(cast_block.operations, typ, arg))
+        assert i_arg == len(inputargs)
+        return cast_block, output_args
+        
+    def _get_resume_cast_block(self, erased_vars, exact_types):
+        # returns something that you should add a link to (with
+        # recloseblock), and the output_args to use in that link.
+        key = tuple(exact_types)
+        if key in self.curr_graph_resume_cast_blocks:
+            cast_block, output_args = self.curr_graph_resume_cast_blocks[key]
+            if SAVE_STATISTICS:
+                self.stats.saved_cast_ops += len(cast_block.operations)
+            return cast_block, output_args
+        else:
+            cast_block, output_args = self._make_cast_block(erased_vars, exact_types)
+            cast_block.inputargs.insert(0, varoftype(lltype.Signed))
+            cast_block.exitswitch = cast_block.inputargs[0]
+            self.curr_graph_resume_cast_blocks[key] = cast_block, output_args
+            return cast_block, output_args
         
     def generate_restart_infos(self, graph):
         restartinfo = frame.RestartInfo(graph, len(self.resume_blocks))



More information about the Pypy-commit mailing list