Commits

Hakan Ardo  committed 9d324e8

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.

  • Participants
  • Parent commits 5fe8f7e
  • Branches jit-fromstart

Comments (0)

Files changed (10)

File pypy/jit/metainterp/optimizeopt/fficall.py

     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?
 

File pypy/jit/metainterp/optimizeopt/heap.py

         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()

File pypy/jit/metainterp/optimizeopt/intbounds.py

         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():

File pypy/jit/metainterp/optimizeopt/intutils.py

         return r
     
     def add(self, offset):
-        res = self.copy()
+        res = self.clone()
         try:
             res.lower = ovfcheck(res.lower + offset)
         except OverflowError:
         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)
         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)
             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

File pypy/jit/metainterp/optimizeopt/optimizer.py

     __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
     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
     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
     
 
         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
                 # 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()

File pypy/jit/metainterp/optimizeopt/rewrite.py

        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)

File pypy/jit/metainterp/optimizeopt/string.py

     "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)

File pypy/jit/metainterp/optimizeopt/unroll.py

         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:
                 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():
                                 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()
+                        
                             
                                 
                 
     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 = []
         # 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]
 
         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
                     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):
         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:

File pypy/jit/metainterp/optimizeopt/virtualize.py

 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)

File pypy/jit/metainterp/test/test_basic.py

                           '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: