[pypy-svn] pypy jit-fromstart: Allow the entire optimizer and its state to be cloned to allow the optimization to be resumed at any point (work in progress). This is used by the unroller to optimize the loop twice. The second time around the full set of inputargs are known from the start and these can be passed on to the bridges through the failargs.

hakanardo commits-noreply at bitbucket.org
Tue Feb 1 20:08:54 CET 2011


Author: Hakan Ardo <hakan at debian.org>
Branch: jit-fromstart
Changeset: r41529:9d324e858411
Date: 2011-02-01 20:08 +0100
http://bitbucket.org/pypy/pypy/changeset/9d324e858411/

Log:	Allow the entire optimizer and its state to be cloned to allow the
	optimization to be resumed at any point (work in progress). This is
	used by the unroller to optimize the loop twice. The second time
	around the full set of inputargs are known from the start and these
	can be passed on to the bridges through the failargs.

diff --git a/pypy/jit/metainterp/optimizeopt/fficall.py b/pypy/jit/metainterp/optimizeopt/fficall.py
--- a/pypy/jit/metainterp/optimizeopt/fficall.py
+++ b/pypy/jit/metainterp/optimizeopt/fficall.py
@@ -67,7 +67,7 @@
     def __init__(self):
         self.funcinfo = None
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
+    def clone_for_next_iteration(self, optimizer, valuemap):
         return OptFfiCall()
         # FIXME: Should any status be saved for next iteration?
 

diff --git a/pypy/jit/metainterp/optimizeopt/string.py b/pypy/jit/metainterp/optimizeopt/string.py
--- a/pypy/jit/metainterp/optimizeopt/string.py
+++ b/pypy/jit/metainterp/optimizeopt/string.py
@@ -367,9 +367,8 @@
     "Handling of strings and unicodes."
     enabled = True
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        self.enabled = True
-        return self
+    def clone_for_next_iteration(self, optimizer, valuemap):
+        return OptString()
     
     def make_vstring_plain(self, box, source_op, mode):
         vvalue = VStringPlainValue(self.optimizer, box, source_op, mode)

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
@@ -155,61 +155,29 @@
         if jumpop:
             assert jumpop.getdescr() is loop.token
             loop.preamble.operations = self.optimizer.newoperations
-            self.optimizer = self.optimizer.reconstruct_for_next_iteration()
+            jump_args = jumpop.getarglist()
+            jumpop.initarglist([])            
 
-            jump_args = jumpop.getarglist()
-            #for i in range(len(jump_args)):
-            #    self.getvalue(jump_args[i]).start_index = i
-
-            jumpop.initarglist([])            
+            preamble_optmizer = self.optimizer
+            self.optimizer = preamble_optmizer.clone_for_next_iteration()
             inputargs = self.inline(self.cloned_operations,
                                     loop.inputargs, jump_args)
+
+            loop_inputargs = loop.inputargs
             loop.inputargs = inputargs
             jmp = ResOperation(rop.JUMP, loop.inputargs[:], None)
             jmp.setdescr(loop.token)
             loop.preamble.operations.append(jmp)
-
             loop.operations = self.optimizer.newoperations
 
-            start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable()
-            assert isinstance(start_resumedescr, ResumeGuardDescr)
-            snapshot = start_resumedescr.rd_snapshot
-            while snapshot is not None:
-                snapshot_args = snapshot.boxes 
-                new_snapshot_args = []
-                for a in snapshot_args:
-                    if not isinstance(a, Const):
-                        a = loop.preamble.inputargs[jump_args.index(a)]
-                    new_snapshot_args.append(a)
-                snapshot.boxes = new_snapshot_args
-                snapshot = snapshot.prev
-
-            short = self.create_short_preamble(loop.preamble, loop)
-            if short:
+            short_loop = self.create_short_preamble(loop.preamble, loop,
+                                                    jump_args)
+            if short_loop:
                 if False:
                     # FIXME: This should save some memory but requires
-                    # a lot of tests to be fixed...
-                    loop.preamble.operations = short[:]
-
-                # Turn guards into conditional jumps to the preamble
-                for i in range(len(short)):
-                    op = short[i]
-                    if op.is_guard():
-                        op = op.clone()
-                        op.setfailargs(None)
-                        op.setdescr(start_resumedescr.clone_if_mutable())
-                        short[i] = op
-
-                short_loop = TreeLoop('short preamble')
-                short_loop.inputargs = loop.preamble.inputargs[:]
-                short_loop.operations = short
-
-                # Clone ops and boxes to get private versions and 
-                newargs = [a.clonebox() for a in short_loop.inputargs]
-                inliner = Inliner(short_loop.inputargs, newargs)
-                short_loop.inputargs = newargs
-                ops = [inliner.inline_op(op) for op in short_loop.operations]
-                short_loop.operations = ops
+                    # a lot of tests to be fixed, and the guards to be
+                    # replaced with ResumeGuardDescr
+                    loop.preamble.operations = short_loop.operations[:]
 
                 assert isinstance(loop.preamble.token, LoopToken)
                 if loop.preamble.token.short_preamble:
@@ -217,12 +185,14 @@
                 else:
                     loop.preamble.token.short_preamble = [short_loop]
 
-                # Forget the values to allow them to be freed
-                for box in short_loop.inputargs:
-                    box.forget_value()
-                for op in short_loop.operations:
-                    if op.result:
-                        op.result.forget_value()
+                if True:
+                    self.optimizer = \
+                            preamble_optmizer.clone_for_next_iteration()
+                    self.optimizer.extraargs = loop.inputargs
+                    loop.inputargs = self.inline(self.cloned_operations,
+                                             loop_inputargs, jump_args)
+                    assert loop.inputargs == inputargs
+                    loop.operations = self.optimizer.newoperations
 
                 for op in loop.operations:
                     if op.is_guard():
@@ -235,6 +205,21 @@
                                 i = -1
                             descr.start_indexes.append(i)
                         descr.parent_short_preamble = short_loop
+                        
+                # Clone ops and boxes to get private versions  
+                newargs = [a.clonebox() for a in short_loop.inputargs]
+                inliner = Inliner(short_loop.inputargs, newargs)
+                short_loop.inputargs = newargs
+                ops = [inliner.inline_op(op) for op in short_loop.operations]
+                short_loop.operations = ops
+
+                # Forget the values to allow them to be freed
+                for box in short_loop.inputargs:
+                    box.forget_value()
+                for op in short_loop.operations:
+                    if op.result:
+                        op.result.forget_value()
+                        
                             
                                 
                 
@@ -242,10 +227,7 @@
     def inline(self, loop_operations, loop_args, jump_args):
         self.inliner = inliner = Inliner(loop_args, jump_args)
            
-        for v in self.optimizer.values.values():
-            v.last_guard_index = -1 # FIXME: Are there any more indexes stored?
-
-        self.optimizer.extraargs = inputargs = []
+        inputargs = []
         seen_inputargs = {}
         for arg in jump_args:
             boxes = []
@@ -257,30 +239,13 @@
         # This loop is equivalent to the main optimization loop in
         # Optimizer.propagate_all_forward
         boxes_created_this_iteration = {}
-        last_emitted_len = -1
         for newop in loop_operations:
             if newop.getopnum() == rop.JUMP:
                 newop.initarglist(inputargs)
-            newop = inliner.inline_op(newop, clone=False)
+            newop = inliner.inline_op(newop) #, clone=False)
             
             self.optimizer.first_optimization.propagate_forward(newop)
 
-            # This should take care of updating inputargs in most cases i.e.
-            # when newoperations is not reorder too much. The rest should be
-            # pacthed below. Bridges will only be able to inherit the part of
-            # the short preamble that produces the boxes that are placed in 
-            # inputargs before the guard producing the bridge is emitted.
-            i = max(last_emitted_len - 2, 0)
-            while i < len(self.optimizer.newoperations):
-                op = self.optimizer.newoperations[i]
-                boxes_created_this_iteration[op.result] = True
-                for a in op.getarglist():
-                    if not isinstance(a, Const) and not a in boxes_created_this_iteration:
-                        if a not in inputargs:
-                            inputargs.append(a)
-                i += 1
-            last_emitted_len = len(self.optimizer.newoperations)
-
         # Remove jump to make sure forced code are placed before it
         newoperations = self.optimizer.newoperations
         jmp = newoperations[-1]
@@ -335,7 +300,7 @@
 
         return True
 
-    def create_short_preamble(self, preamble, loop):
+    def create_short_preamble(self, preamble, loop, original_jump_args):
         #return None # Dissable
 
         preamble_ops = preamble.operations
@@ -417,8 +382,36 @@
                     return None
             if op.result:
                 seen[op.result] = True
-        
-        return short_preamble
+
+        # Make all guards resume at the top of the preamble
+        start_resumedescr = loop.preamble.start_resumedescr
+        start_resumedescr = start_resumedescr.clone_if_mutable()
+        assert isinstance(start_resumedescr, ResumeGuardDescr)
+        snapshot = start_resumedescr.rd_snapshot
+        while snapshot is not None:
+            snapshot_args = snapshot.boxes 
+            new_snapshot_args = []
+            for a in snapshot_args:
+                if not isinstance(a, Const):
+                    a = loop.preamble.inputargs[original_jump_args.index(a)]
+                new_snapshot_args.append(a)
+            snapshot.boxes = new_snapshot_args
+            snapshot = snapshot.prev
+
+        for i in range(len(short_preamble)):
+            op = short_preamble[i]
+            if op.is_guard():
+                op = op.clone()
+                op.setfailargs(None)
+                op.setdescr(start_resumedescr.clone_if_mutable())
+                short_preamble[i] = op
+
+        # Contruct the TreeLoop object
+        short_loop = TreeLoop('short preamble')
+        short_loop.inputargs = loop.preamble.inputargs[:]
+        short_loop.operations = short_preamble
+                
+        return short_loop
 
 class ExeState(object):
     def __init__(self, optimizer):
@@ -543,8 +536,8 @@
         return self.map[loopbox]
 
 class OptInlineShortPreamble(Optimization):
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        return self
+    def clone_for_next_iteration(self, optimizer, valuemap):
+        return OptInlineShortPreamble()
     
     def propagate_forward(self, op):
         if op.getopnum() == rop.JUMP:

diff --git a/pypy/jit/metainterp/optimizeopt/heap.py b/pypy/jit/metainterp/optimizeopt/heap.py
--- a/pypy/jit/metainterp/optimizeopt/heap.py
+++ b/pypy/jit/metainterp/optimizeopt/heap.py
@@ -24,8 +24,10 @@
         self.lazy_setfields = {}
         self.lazy_setfields_descrs = []     # keys (at least) of previous dict
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
+    def clone_for_next_iteration(self, optimizer, valuemap):
         new = OptHeap()
+        return new
+        # FIXME:
 
         if True:
             self.force_all_lazy_setfields()

diff --git a/pypy/jit/metainterp/optimizeopt/rewrite.py b/pypy/jit/metainterp/optimizeopt/rewrite.py
--- a/pypy/jit/metainterp/optimizeopt/rewrite.py
+++ b/pypy/jit/metainterp/optimizeopt/rewrite.py
@@ -13,8 +13,8 @@
        This includes already executed operations and constants.
     """
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        return self
+    def clone_for_next_iteration(self, optimizer, valuemap):
+        return OptRewrite()
     
     def propagate_forward(self, op):
         args = self.optimizer.make_args_key(op)

diff --git a/pypy/jit/metainterp/optimizeopt/optimizer.py b/pypy/jit/metainterp/optimizeopt/optimizer.py
--- a/pypy/jit/metainterp/optimizeopt/optimizer.py
+++ b/pypy/jit/metainterp/optimizeopt/optimizer.py
@@ -27,15 +27,19 @@
     __metaclass__ = extendabletype
     _attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound')
     last_guard_index = -1
-    #start_index = -1
 
     level = LEVEL_UNKNOWN
     known_class = None
     intbound = None
 
-    def __init__(self, box):
+    def __init__(self, box, level=None, known_class=None, intbound=None):
         self.box = box
-        self.intbound = IntBound(MININT, MAXINT) #IntUnbounded()
+        self.level = level
+        self.known_class = known_class
+        if intbound:
+            self.intbound = intbound
+        else:
+            self.intbound = IntBound(MININT, MAXINT) #IntUnbounded()
         if isinstance(box, Const):
             self.make_constant(box)
         # invariant: box is a Const if and only if level == LEVEL_CONSTANT
@@ -55,13 +59,17 @@
     def get_reconstructed(self, optimizer, valuemap):
         if self in valuemap:
             return valuemap[self]
-        new = self.reconstruct_for_next_iteration(optimizer)
+        new = self.clone_for_next_iteration(optimizer)
+        assert new.__class__ == self.__class__
         valuemap[self] = new
         self.reconstruct_childs(new, valuemap)
         return new
 
-    def reconstruct_for_next_iteration(self, optimizer):
-        return self
+    def clone_for_next_iteration(self, optimizer):
+        # last_guard_index should not be propagated here as the list into
+        # which it is an index is cleared
+        return OptValue(self.box, self.level, self.known_class,
+                        self.intbound.clone())
 
     def reconstruct_childs(self, new, valuemap):
         pass
@@ -224,8 +232,7 @@
     def turned_constant(self, value):
         pass
 
-    def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None):
-        #return self.__class__()
+    def clone_for_next_iteration(self, optimizer=None, valuemap=None):
         raise NotImplementedError
     
 
@@ -304,31 +311,39 @@
         for o in self.optimizations:
             o.force_at_end_of_preamble()
             
-    def reconstruct_for_next_iteration(self, optimizer=None, valuemap=None):
+    def clone_for_next_iteration(self, optimizer=None, valuemap=None):
         assert optimizer is None
         assert valuemap is None
         valuemap = {}
         new = Optimizer(self.metainterp_sd, self.loop)
-        optimizations = [o.reconstruct_for_next_iteration(new, valuemap) for o in 
+        new.values = {}
+        optimizations = [o.clone_for_next_iteration(new, valuemap) for o in 
                          self.optimizations]
         new.set_optimizations(optimizations)
 
-        new.values = {}
-        for box, value in self.values.items():
-            new.values[box] = value.get_reconstructed(new, valuemap)
-        new.interned_refs = self.interned_refs
-        new.bool_boxes = {}
-        for value in new.bool_boxes.keys():
-            new.bool_boxes[value.get_reconstructed(new, valuemap)] = None
+        # FIXME: new.interned_refs = self.interned_refs
+        # FIXME:
+        #new.bool_boxes = {}
+        #for value in new.bool_boxes.keys():
+        #    new.bool_boxes[value.get_reconstructed(new, valuemap)] = None
 
         # FIXME: Move to rewrite.py
         new.loop_invariant_results = {}
         for key, value in self.loop_invariant_results.items():
             new.loop_invariant_results[key] = \
                                  value.get_reconstructed(new, valuemap)
+
+        for args, op in self.pure_operations.items():
+            newargs = args[:]
+            for i in range(len(newargs)):
+                if isinstance(newargs[i], OptValue):
+                    newargs[i] = newargs[i].get_reconstructed(new, valuemap)
+            v = self.getvalue(op.result)
+            new.values[op.result] = v.get_reconstructed(new, valuemap)
+            new.pure_operations[newargs] = op
+            # FIXME: This will not work for ops with mutable descr
             
-        new.pure_operations = self.pure_operations
-        new.producer = self.producer
+        # FIXME: Any point in propagating these? new.producer = self.producer
         assert self.posponedop is None
 
         return new
@@ -539,14 +554,15 @@
                 # all constant arguments: constant-fold away
                 argboxes = [self.get_constant_box(op.getarg(i))
                             for i in range(op.numargs())]
-                resbox = execute_nonspec(self.cpu, None,
-                                         op.getopnum(), argboxes, op.getdescr())
+                resbox = execute_nonspec(self.cpu, None, op.getopnum(),
+                                         argboxes, op.getdescr())
                 # FIXME: Don't we need to check for an overflow here?
                 self.make_constant(op.result, resbox.constbox())
                 return
 
             # did we do the exact same operation already?
             args = self.make_args_key(op)
+            
             oldop = self.pure_operations.get(args, None)
             if oldop is not None and oldop.getdescr() is op.getdescr():
                 assert oldop.getopnum() == op.getopnum()

diff --git a/pypy/jit/metainterp/test/test_basic.py b/pypy/jit/metainterp/test/test_basic.py
--- a/pypy/jit/metainterp/test/test_basic.py
+++ b/pypy/jit/metainterp/test/test_basic.py
@@ -403,6 +403,26 @@
                           'jump': 2,
                           'int_gt': 1, 'guard_true': 1})
 
+    def test_loop_invariant_mul_bridge_maintaining3(self):
+        myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
+        def f(x, y):
+            res = 0
+            while y > 0:
+                myjitdriver.can_enter_jit(x=x, y=y, res=res)
+                myjitdriver.jit_merge_point(x=x, y=y, res=res)
+                if y<16:
+                    res += 1
+                res += x * x
+                y -= 1
+            return res
+        res = self.meta_interp(f, [6, 32])
+        assert res == 1167
+        self.check_loop_count(3)
+        self.check_loops({'int_add': 3, 'int_lt': 1,
+                          'int_sub': 2, 'guard_false': 1,
+                          'jump': 2,
+                          'int_gt': 1, 'guard_true': 1})
+
     def test_loop_invariant_intbox(self):
         myjitdriver = JitDriver(greens = [], reds = ['y', 'res', 'x'])
         class I:

diff --git a/pypy/jit/metainterp/optimizeopt/intbounds.py b/pypy/jit/metainterp/optimizeopt/intbounds.py
--- a/pypy/jit/metainterp/optimizeopt/intbounds.py
+++ b/pypy/jit/metainterp/optimizeopt/intbounds.py
@@ -13,9 +13,9 @@
         self.posponedop = None
         self.nextop = None
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
+    def clone_for_next_iteration(self, optimizer, valuemap):
         assert self.posponedop is None
-        return self 
+        return OptIntBounds()
 
     def propagate_forward(self, op):
         if op.is_ovf():

diff --git a/pypy/jit/metainterp/optimizeopt/intutils.py b/pypy/jit/metainterp/optimizeopt/intutils.py
--- a/pypy/jit/metainterp/optimizeopt/intutils.py
+++ b/pypy/jit/metainterp/optimizeopt/intutils.py
@@ -76,7 +76,7 @@
         return r
     
     def add(self, offset):
-        res = self.copy()
+        res = self.clone()
         try:
             res.lower = ovfcheck(res.lower + offset)
         except OverflowError:
@@ -91,7 +91,7 @@
         return self.mul_bound(IntBound(value, value))
     
     def add_bound(self, other):
-        res = self.copy()
+        res = self.clone()
         if other.has_upper:
             try:
                 res.upper = ovfcheck(res.upper + other.upper)
@@ -109,7 +109,7 @@
         return res
 
     def sub_bound(self, other):
-        res = self.copy()
+        res = self.clone()
         if other.has_lower:
             try:
                 res.upper = ovfcheck(res.upper - other.lower)
@@ -204,7 +204,7 @@
             u = 'Inf'
         return '%s <= x <= %s' % (l, u)
 
-    def copy(self):
+    def clone(self):
         res = IntBound(self.lower, self.upper)
         res.has_lower = self.has_lower
         res.has_upper = self.has_upper

diff --git a/pypy/jit/metainterp/optimizeopt/virtualize.py b/pypy/jit/metainterp/optimizeopt/virtualize.py
--- a/pypy/jit/metainterp/optimizeopt/virtualize.py
+++ b/pypy/jit/metainterp/optimizeopt/virtualize.py
@@ -252,8 +252,8 @@
 class OptVirtualize(optimizer.Optimization):
     "Virtualize objects until they escape."
 
-    def reconstruct_for_next_iteration(self, optimizer, valuemap):
-        return self
+    def clone_for_next_iteration(self, optimizer, valuemap):
+        return OptVirtualize()
 
     def make_virtual(self, known_class, box, source_op=None):
         vvalue = VirtualValue(self.optimizer, known_class, box, source_op)


More information about the Pypy-commit mailing list