Commits

Hakan Ardo  committed eb7cfad

- Dont cache setfields across loop boundaries we dont want them in the
short preamble.
- Dissabled cached_arrayitems support for now.
- Make the short preamble guard for the state of all values from the
preamble surviving into the loop. They can influence the loop
without turning up as arguments by introducing assumptions such as
for eaxmple getfield(p1)==7. The vaues in the original inputargs are
already treated by the VirtualState so they are ignored here.

  • Participants
  • Parent commits 1938c6d
  • Branches jit-short_from_state

Comments (0)

Files changed (4)

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

         #      'cached_fields'.
         #
         self._cached_fields = {}
+        self._cached_fields_getfield_op = {}
         self._lazy_setfield = None
         self._lazy_setfield_registered = False
 
         if cached_fieldvalue is not fieldvalue:
             # common case: store the 'op' as lazy_setfield, and register
             # myself in the optheap's _lazy_setfields list
+            try:
+                del self._cached_fields_getfield_op[structvalue]
+            except KeyError:
+                pass
             self._lazy_setfield = op
             if not self._lazy_setfield_registered:
                 optheap._lazy_setfields.append(self)
         else:
             return self._cached_fields.get(structvalue, None)
 
-    def remember_field_value(self, structvalue, fieldvalue):
+    def remember_field_value(self, structvalue, fieldvalue, getfield_op=None):
         assert self._lazy_setfield is None
         self._cached_fields[structvalue] = fieldvalue
+        self._cached_fields_getfield_op[structvalue] = getfield_op
 
     def force_lazy_setfield(self, optheap):
         op = self._lazy_setfield
             # setfield might impact any of the stored result (because of
             # possible aliasing).
             self._cached_fields.clear()
+            self._cached_fields_getfield_op.clear()
             self._lazy_setfield = None
             optheap.next_optimization.propagate_forward(op)
             # Once it is done, we can put at least one piece of information
         assert self._lazy_setfield is None
         cf = CachedField()
         for structvalue, fieldvalue in self._cached_fields.iteritems():
-            structvalue2 = structvalue.get_cloned(optimizer, valuemap)
-            fieldvalue2  = fieldvalue .get_cloned(optimizer, valuemap)
-            cf._cached_fields[structvalue2] = fieldvalue2
+            op = self._cached_fields_getfield_op.get(structvalue, None)
+            if op:
+                structvalue2 = structvalue.get_cloned(optimizer, valuemap)
+                fieldvalue2  = fieldvalue .get_cloned(optimizer, valuemap)
+                cf._cached_fields[structvalue2] = fieldvalue2
         return cf
 
-    def produce_potential_short_preamble_ops(self, potential_ops, descr):
-        for structvalue, fieldvalue in self._cached_fields.iteritems():
-            result = fieldvalue.get_key_box()
-            potential_ops[result] = ResOperation(rop.GETFIELD_GC,
-                                                 [structvalue.get_key_box()],
-                                                 result, descr)
+    def produce_potential_short_preamble_ops(self, optimizer,
+                                             potential_ops, descr):
+        for structvalue, op in self._cached_fields_getfield_op.iteritems():
+            if op and structvalue in self._cached_fields:
+                potential_ops[op.result] = op
                     
 
 class CachedArrayItems(object):
         for descr, d in self.cached_fields.items():
             new.cached_fields[descr] = d.get_cloned(optimizer, valuemap)
 
+        return new
+
         new.cached_arrayitems = {}
         for descr, d in self.cached_arrayitems.items():
             newd = {}
 
     def produce_potential_short_preamble_ops(self, potential_ops):        
         for descr, d in self.cached_fields.items():
-            d.produce_potential_short_preamble_ops(potential_ops, descr)
+            d.produce_potential_short_preamble_ops(self.optimizer,
+                                                   potential_ops, descr)
+
+        # FIXME
         for descr, d in self.cached_arrayitems.items():
             for value, cache in d.items():
                 for index, fieldvalue in cache.fixed_index_items.items():
                     try:
                         cf = self.cached_fields[fielddescr]
                         cf._cached_fields.clear()
+                        cf._cached_fields_getfield_op.clear()
                     except KeyError:
                         pass
                 for arraydescr in effectinfo.write_descrs_arrays:
         fieldvalue = cf.getfield_from_cache(self, structvalue)
         if fieldvalue is not None:
             self.make_equal_to(op.result, fieldvalue)
-            return
-        # default case: produce the operation
-        structvalue.ensure_nonnull()
-        ###self.optimizer.optimize_default(op)
-        self.emit_operation(op)
+        else:
+            # default case: produce the operation
+            structvalue.ensure_nonnull()
+            ###self.optimizer.optimize_default(op)
+            self.emit_operation(op)
         # then remember the result of reading the field
         fieldvalue = self.getvalue(op.result)
-        cf.remember_field_value(structvalue, fieldvalue)
+        cf.remember_field_value(structvalue, fieldvalue, op)
 
     def optimize_SETFIELD_GC(self, op):
         if self.has_pure_result(rop.GETFIELD_GC_PURE, [op.getarg(0)],

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

             self.make_constant(box)
         # invariant: box is a Const if and only if level == LEVEL_CONSTANT
 
+    def make_guards(self, box):
+        guards = []
+        if self.level == LEVEL_CONSTANT:
+            op = ResOperation(rop.GUARD_VALUE, [box, self.box], None)
+            guards.append(op)
+        elif self.level == LEVEL_KNOWNCLASS:
+            op = ResOperation(rop.GUARD_CLASS, [box, self.known_class], None)
+            guards.append(op)
+        else:
+            if self.level == LEVEL_NONNULL:
+                op = ResOperation(rop.GUARD_NONNULL, [box], None)
+                guards.append(op)
+            if self.intbound.has_lower:
+                bound = self.intbound.lower
+                res = BoxInt()
+                op = ResOperation(rop.INT_GE, [box, ConstInt(bound)], res)
+                guards.append(op)
+                op = ResOperation(rop.GUARD_TRUE, [res], None)
+                guards.append(op)
+            if self.intbound.has_upper:
+                bound = self.intbound.upper
+                res = BoxInt()
+                op = ResOperation(rop.INT_LE, [box, ConstInt(bound)], res)
+                guards.append(op)
+                op = ResOperation(rop.GUARD_TRUE, [res], None)
+                guards.append(op)
+        return guards
+
     def force_box(self):
         return self.box
 
     def produce_short_preamble_box(self, box, short_boxes, potential_ops):
         if box in short_boxes:
             return 
-        if self.getvalue(box).is_constant():
+        if isinstance(box, Const): #self.getvalue(box).is_constant():
             return 
         if box in potential_ops:
             op = potential_ops[box]

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

             inputargs = virtual_state.make_inputargs(values)
             sb = preamble_optimizer.produce_short_preamble_ops(inputargs)
             self.short_boxes = sb
+        
             initial_inputargs_len = len(inputargs)
-            
 
             inputargs, short = self.inline(self.cloned_operations,
                                            loop.inputargs, jump_args,
 
         self.short_inliner = Inliner(inputargs, jumpargs)
         short = []
+        short_seen = {}
+        for result, op in self.short_boxes.items():
+            if op is not None:
+                for op in self.getvalue(result).make_guards(result):
+                    self.add_op_to_short(op, short, short_seen)
 
         i = j = 0
         while i < len(self.optimizer.newoperations):
             op = self.optimizer.newoperations[i]
+            
             self.boxes_created_this_iteration[op.result] = True
             args = op.getarglist()
             if op.is_guard():
             
             for a in args:
                 self.import_box(a, inputargs, short, short_jumpargs,
-                                jumpargs, True)
+                                jumpargs, short_seen)
             i += 1
 
             if i == len(self.optimizer.newoperations):
                 while j < len(jumpargs):
                     a = jumpargs[j]
                     self.import_box(a, inputargs, short, short_jumpargs,
-                                    jumpargs, True)
+                                    jumpargs, short_seen)
                     j += 1
 
         jumpop.initarglist(jumpargs)
         short.append(ResOperation(rop.JUMP, short_jumpargs, None))
         return inputargs, short
 
+    def add_op_to_short(self, op, short, short_seen):
+        if op is None:
+            return 
+        if op.result is not None and op.result in short_seen:
+            return self.short_inliner.inline_arg(op.result)
+        for a in op.getarglist():
+            if not isinstance(a, Const) and a not in short_seen:
+                self.add_op_to_short(self.short_boxes[a], short, short_seen)
+        short.append(op)
+        short_seen[op.result] = True
+        newop = self.short_inliner.inline_op(op)
+        self.optimizer.send_extra_operation(newop)
+        assert self.optimizer.newoperations[-1] is not newop
+
+        if op.is_ovf():
+            # FIXME: ensure that GUARD_OVERFLOW:ed ops not end up here
+            guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
+            short.append(guard)
+            self.optimizer.send_extra_operation(guard)
+            assert self.optimizer.newoperations[-1] is not guard
+
+        # FIXME: Emit a proper guards here in case it is not
+        #        removed by the optimizer. Can that happen?
+        return newop.result
+        
     def import_box(self, box, inputargs, short, short_jumpargs,
-                   jumpargs, extend_inputargs):
+                   jumpargs, short_seen):
         if isinstance(box, Const) or box in inputargs:
             return
         if box in self.boxes_created_this_iteration:
             return
 
         short_op = self.short_boxes[box]
-        for a in short_op.getarglist():
-            self.import_box(a, inputargs, short, short_jumpargs, jumpargs, False)
-        short.append(short_op)
-        newop = self.short_inliner.inline_op(short_op)
-        self.optimizer.send_extra_operation(newop)
-        if newop.is_ovf():
-            # FIXME: ensure that GUARD_OVERFLOW:ed ops not end up here
-            guard = ResOperation(rop.GUARD_NO_OVERFLOW, [], None)
-            short.append(guard)
-            # FIXME: Emit a proper guard here in case it is not
-            #        removed by the optimizer. Can that happen?
-            self.optimizer.send_extra_operation(guard)
-            assert self.optimizer.newoperations[-1] is not guard
-
-        if extend_inputargs:
-            short_jumpargs.append(short_op.result)
-            inputargs.append(box)
-            box = newop.result
-            if box in self.optimizer.values:
-                box = self.optimizer.values[box].force_box()
-            jumpargs.append(box)
+        newresult = self.add_op_to_short(short_op, short, short_seen)
+        
+        short_jumpargs.append(short_op.result)
+        inputargs.append(box)
+        box = newresult
+        if box in self.optimizer.values:
+            box = self.optimizer.values[box].force_box()
+        jumpargs.append(box)
         
     def sameop(self, op1, op2):
         if op1.getopnum() != op2.getopnum():
 
         for op in loop_operations:
             newop = inliner.inline_op(op)
-            
             if not dryrun:
                 self.emit_operation(newop)
             else:

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

         res = self.meta_interp(f, [32, 7])
         assert res == f(32, 7)
 
+    def test_getfield_result_constant(self):
+        myjitdriver = JitDriver(greens = [], reds = ['sa', 'i', 'n', 'a', 'node'])
+        class A:
+            pass
+        def f(n, a):
+            i = sa = 0
+            node = A()
+            node.val1 = 7
+            while i < n:
+                myjitdriver.can_enter_jit(sa=sa, i=i, n=n, a=a, node=node)
+                myjitdriver.jit_merge_point(sa=sa, i=i, n=n, a=a, node=node)
+                if node.val1 == 7:
+                    sa += 1
+                if i > n/2:
+                    node.val1 = -7
+                i += 1
+            return sa
+        res = self.meta_interp(f, [32, 7])
+        assert res == f(32, 7)
+
 
 
 class TestOOtype(BasicTests, OOJitMixin):