[pypy-svn] pypy default: This should prevent the short preamble from beeing inlined if the virtuals needed to call the loop cannot be virtuals (ie some pointer to them have escaped). Instead the loop will be retraced and a new specialized version generated. If this too fails, jump to the preamble instead. Also short preambles are nolonger generated for cases with virtuals in the arguments with members that are constants. To handle that case we need to also check that the values of those constants are the same at the end of the bridge.

hakanardo commits-noreply at bitbucket.org
Wed Feb 2 20:48:18 CET 2011


Author: Hakan Ardo <hakan at debian.org>
Branch: 
Changeset: r41576:6c956838e89c
Date: 2011-02-02 20:36 +0100
http://bitbucket.org/pypy/pypy/changeset/6c956838e89c/

Log:	This should prevent the short preamble from beeing inlined if the
	virtuals needed to call the loop cannot be virtuals (ie some pointer
	to them have escaped). Instead the loop will be retraced and a new
	specialized version generated. If this too fails, jump to the
	preamble instead. Also short preambles are nolonger generated for
	cases with virtuals in the arguments with members that are
	constants. To handle that case we need to also check that the values
	of those constants are the same at the end of the bridge.

diff --git a/pypy/jit/metainterp/optimizeopt/unroll.py b/pypy/jit/metainterp/optimizeopt/unroll.py
--- a/pypy/jit/metainterp/optimizeopt/unroll.py
+++ b/pypy/jit/metainterp/optimizeopt/unroll.py
@@ -139,6 +139,7 @@
             self.cloned_operations.append(newop)
             
     def propagate_all_forward(self):
+        self.make_short_preamble = True
         loop = self.optimizer.loop
         jumpop = loop.operations[-1]
         if jumpop.getopnum() == rop.JUMP:
@@ -151,11 +152,12 @@
 
         if jumpop:
             assert jumpop.getdescr() is loop.token
+            jump_args = jumpop.getarglist()
+            jumpop.initarglist([])
+            virtual_state = [self.getvalue(a).is_virtual() for a in jump_args]
+
             loop.preamble.operations = self.optimizer.newoperations
             self.optimizer = self.optimizer.reconstruct_for_next_iteration()
-
-            jump_args = jumpop.getarglist()
-            jumpop.initarglist([])            
             inputargs = self.inline(self.cloned_operations,
                                     loop.inputargs, jump_args)
             loop.inputargs = inputargs
@@ -210,6 +212,7 @@
                     loop.preamble.token.short_preamble.append(short_loop)
                 else:
                     loop.preamble.token.short_preamble = [short_loop]
+                short_loop.virtual_state = virtual_state
 
                 # Forget the values to allow them to be freed
                 for box in short_loop.inputargs:
@@ -233,6 +236,8 @@
             for a in boxes:
                 if not isinstance(a, Const):
                     inputargs.append(a)
+                else:
+                    self.make_short_preamble = False
 
         # This loop is equivalent to the main optimization loop in
         # Optimizer.propagate_all_forward
@@ -299,6 +304,8 @@
         return True
 
     def create_short_preamble(self, preamble, loop):
+        if not self.make_short_preamble:
+            return None
         #return None # Dissable
 
         preamble_ops = preamble.operations
@@ -506,6 +513,10 @@
         return self.map[loopbox]
 
 class OptInlineShortPreamble(Optimization):
+    def __init__(self, retraced):
+        self.retraced = retraced
+        
+    
     def reconstruct_for_next_iteration(self, optimizer, valuemap):
         return self
     
@@ -520,19 +531,34 @@
             # handle and the inlining fails unexpectedly belwo.
             short = descr.short_preamble
             if short:
+                args = op.getarglist()
+                virtual_state = [self.getvalue(a).is_virtual() for a in args]
                 for sh in short:
-                    if self.inline(sh.operations, sh.inputargs,
-                                   op.getarglist(), dryrun=True):
-                        try:
-                            self.inline(sh.operations, sh.inputargs,
-                                        op.getarglist())
-                        except InvalidLoop:
-                            debug_print("Inlining failed unexpectedly",
-                                        "jumping to preamble instead")
-                            self.emit_operation(op)
-                        return
-                    
-                raise RetraceLoop
+                    assert len(virtual_state) == len(sh.virtual_state)
+                    for i in range(len(virtual_state)):
+                        if sh.virtual_state[i] and not virtual_state[i]:
+                            break
+                        elif not sh.virtual_state[i] and virtual_state[i]:
+                            # XXX Here, this bridge has made some box virtual
+                            # that is not virtual in the original loop. These
+                            # will be forced below. However we could choose
+                            # to raise RetraceLoop here to create a new 
+                            # specialized version of the loop where more
+                            # boxes will be virtual.
+                            pass
+                    else:
+                        if self.inline(sh.operations, sh.inputargs,
+                                       op.getarglist(), dryrun=True):
+                            try:
+                                self.inline(sh.operations, sh.inputargs,
+                                            op.getarglist())
+                            except InvalidLoop:
+                                debug_print("Inlining failed unexpectedly",
+                                            "jumping to preamble instead")
+                                self.emit_operation(op)
+                            return
+                if not self.retraced:    
+                    raise RetraceLoop
         self.emit_operation(op)
                 
         

diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py
--- a/pypy/jit/metainterp/compile.py
+++ b/pypy/jit/metainterp/compile.py
@@ -566,7 +566,7 @@
         pass
 
 
-def compile_new_bridge(metainterp, old_loop_tokens, resumekey):
+def compile_new_bridge(metainterp, old_loop_tokens, resumekey, retraced=False):
     """Try to compile a new bridge leading from the beginning of the history
     to some existing place.
     """
@@ -587,8 +587,9 @@
         inline_short_preamble = True
     try:
         target_loop_token = state.optimize_bridge(metainterp_sd,
-                                                  old_loop_tokens,
-                                                  new_loop, inline_short_preamble)
+                                                  old_loop_tokens, new_loop,
+                                                  inline_short_preamble,
+                                                  retraced)
     except InvalidLoop:
         # XXX I am fairly convinced that optimize_bridge cannot actually raise
         # InvalidLoop

diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py
--- a/pypy/jit/metainterp/pyjitpl.py
+++ b/pypy/jit/metainterp/pyjitpl.py
@@ -1929,7 +1929,8 @@
         try:
             target_loop_token = compile.compile_new_bridge(self,
                                                            [loop_token],
-                                                           self.resumekey)
+                                                           self.resumekey,
+                                                           True)
         except RetraceLoop:
             assert False
         assert target_loop_token is not None

diff --git a/pypy/jit/metainterp/simple_optimize.py b/pypy/jit/metainterp/simple_optimize.py
--- a/pypy/jit/metainterp/simple_optimize.py
+++ b/pypy/jit/metainterp/simple_optimize.py
@@ -47,7 +47,8 @@
             jumpop.setdescr(loop.token)
         return None
 
-def optimize_bridge(metainterp_sd, old_loops, loop, inline_short_preamble):
+def optimize_bridge(metainterp_sd, old_loops, loop, inline_short_preamble,
+                    retraced):
     optimize_loop(metainterp_sd, [], loop)
     jumpop = loop.operations[-1]
     if jumpop.getopnum() == rop.JUMP:

diff --git a/pypy/jit/metainterp/nounroll_optimize.py b/pypy/jit/metainterp/nounroll_optimize.py
--- a/pypy/jit/metainterp/nounroll_optimize.py
+++ b/pypy/jit/metainterp/nounroll_optimize.py
@@ -17,7 +17,8 @@
     optimize_loop_1(metainterp_sd, loop, False)
     return None
 
-def optimize_bridge(metainterp_sd, old_loop_tokens, bridge, inline_short_preamble):
+def optimize_bridge(metainterp_sd, old_loop_tokens, bridge,
+                    inline_short_preamble, retraced=False):
     debug_start("jit-optimize")
     try:
         return _optimize_bridge(metainterp_sd, old_loop_tokens, bridge)

diff --git a/pypy/jit/metainterp/optimize.py b/pypy/jit/metainterp/optimize.py
--- a/pypy/jit/metainterp/optimize.py
+++ b/pypy/jit/metainterp/optimize.py
@@ -22,20 +22,24 @@
 
 # ____________________________________________________________
 
-def optimize_bridge(metainterp_sd, old_loop_tokens, bridge, inline_short_preamble=True):
+def optimize_bridge(metainterp_sd, old_loop_tokens, bridge,
+                    inline_short_preamble=True, retraced=False):
     debug_start("jit-optimize")
     try:
-        return _optimize_bridge(metainterp_sd, old_loop_tokens, bridge, inline_short_preamble)
+        return _optimize_bridge(metainterp_sd, old_loop_tokens, bridge,
+                                inline_short_preamble, retraced)
     finally:
         debug_stop("jit-optimize")
 
-def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge, inline_short_preamble):
+def _optimize_bridge(metainterp_sd, old_loop_tokens, bridge,
+                     inline_short_preamble, retraced=False):
     cpu = metainterp_sd.cpu
     metainterp_sd.logger_noopt.log_loop(bridge.inputargs, bridge.operations)
     if old_loop_tokens:
         old_loop_token = old_loop_tokens[0]
         bridge.operations[-1].setdescr(old_loop_token)   # patch jump target
-        optimize_bridge_1(metainterp_sd, bridge, inline_short_preamble)
+        optimize_bridge_1(metainterp_sd, bridge, inline_short_preamble,
+                          retraced)
         return old_loop_tokens[0]
         #return bridge.operations[-1].getdescr()
     return None

diff --git a/pypy/jit/metainterp/optimizeopt/__init__.py b/pypy/jit/metainterp/optimizeopt/__init__.py
--- a/pypy/jit/metainterp/optimizeopt/__init__.py
+++ b/pypy/jit/metainterp/optimizeopt/__init__.py
@@ -6,7 +6,8 @@
 from pypy.jit.metainterp.optimizeopt.string import OptString
 from pypy.jit.metainterp.optimizeopt.unroll import optimize_unroll, OptInlineShortPreamble
 
-def optimize_loop_1(metainterp_sd, loop, unroll=True, inline_short_preamble=True):
+def optimize_loop_1(metainterp_sd, loop, unroll=True,
+                    inline_short_preamble=True, retraced=False):
     """Optimize loop.operations to remove internal overheadish operations. 
     """
     opt_str = OptString()
@@ -17,7 +18,7 @@
                      OptHeap(),
                     ]
     if inline_short_preamble:
-        optimizations = [OptInlineShortPreamble()] +  optimizations
+        optimizations = [OptInlineShortPreamble(retraced)] +  optimizations
         
     if metainterp_sd.jit_ffi:
         from pypy.jit.metainterp.optimizeopt.fficall import OptFfiCall
@@ -33,6 +34,8 @@
         optimizer = Optimizer(metainterp_sd, loop, optimizations)
         optimizer.propagate_all_forward()
 
-def optimize_bridge_1(metainterp_sd, bridge, inline_short_preamble=True):
+def optimize_bridge_1(metainterp_sd, bridge, inline_short_preamble=True,
+                      retraced=False):
     """The same, but for a bridge. """
-    optimize_loop_1(metainterp_sd, bridge, False, inline_short_preamble)
+    optimize_loop_1(metainterp_sd, bridge, False, inline_short_preamble,
+                    retraced)

diff --git a/pypy/jit/metainterp/test/test_virtual.py b/pypy/jit/metainterp/test/test_virtual.py
--- a/pypy/jit/metainterp/test/test_virtual.py
+++ b/pypy/jit/metainterp/test/test_virtual.py
@@ -211,7 +211,7 @@
             return node.value
         res = self.meta_interp(f, [20], policy=StopAtXPolicy(externfn))
         assert res == f(20)
-        self.check_loop_count(2)
+        self.check_loop_count(3)
         self.check_loops(**{self._new_op: 1})
         self.check_loops(int_mul=0, call=1)
 


More information about the Pypy-commit mailing list