Author: Jeremy Thurgood <fir...@gmail.com>
Branch: 
Changeset: r87671:e26ed9527792
Date: 2016-10-09 19:28 +0200
http://bitbucket.org/pypy/pypy/changeset/e26ed9527792/

Log:    Merge unrecursive-opt branch

        This makes optimiseopt iterative instead of recursive so it can be
        reasoned about more easily and debugging is faster.

diff too long, truncating to 2000 out of 2035 lines

diff --git a/rpython/jit/metainterp/optimizeopt/earlyforce.py 
b/rpython/jit/metainterp/optimizeopt/earlyforce.py
--- a/rpython/jit/metainterp/optimizeopt/earlyforce.py
+++ b/rpython/jit/metainterp/optimizeopt/earlyforce.py
@@ -26,7 +26,7 @@
 
             for arg in op.getarglist():
                 self.optimizer.force_box(arg, self)
-        self.emit_operation(op)
+        return self.emit(op)
 
     def setup(self):
         self.optimizer.optearlyforce = self
diff --git a/rpython/jit/metainterp/optimizeopt/heap.py 
b/rpython/jit/metainterp/optimizeopt/heap.py
--- a/rpython/jit/metainterp/optimizeopt/heap.py
+++ b/rpython/jit/metainterp/optimizeopt/heap.py
@@ -128,7 +128,7 @@
                     if a is optheap.postponed_op:
                         optheap.emit_postponed_op()
                         break
-            optheap.next_optimization.propagate_forward(op)
+            optheap.emit_extra(op, emit=False)
             if not can_cache:
                 return
             # Once it is done, we can put at least one piece of information
@@ -259,7 +259,7 @@
         if self.postponed_op:
             postponed_op = self.postponed_op
             self.postponed_op = None
-            self.next_optimization.propagate_forward(postponed_op)
+            self.emit_extra(postponed_op, emit=False)
 
     def produce_potential_short_preamble_ops(self, sb):
         descrkeys = self.cached_fields.keys()
@@ -312,7 +312,7 @@
             cf = submap[index] = ArrayCachedItem(index)
         return cf
 
-    def emit_operation(self, op):        
+    def emit(self, op):
         self.emitting_operation(op)
         self.emit_postponed_op()
         opnum = op.opnum
@@ -320,7 +320,7 @@
             or rop.is_ovf(opnum)):
             self.postponed_op = op
         else:
-            Optimization.emit_operation(self, op)
+            return Optimization.emit(self, op)
 
     def emitting_operation(self, op):
         if rop.has_no_side_effect(op.opnum):
@@ -370,7 +370,7 @@
         if oopspecindex == EffectInfo.OS_DICT_LOOKUP:
             if self._optimize_CALL_DICT_LOOKUP(op):
                 return
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_CALL_F = optimize_CALL_I
     optimize_CALL_R = optimize_CALL_I
     optimize_CALL_N = optimize_CALL_I
@@ -428,7 +428,7 @@
     def optimize_GUARD_NO_EXCEPTION(self, op):
         if self.last_emitted_operation is REMOVED:
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     optimize_GUARD_EXCEPTION = optimize_GUARD_NO_EXCEPTION
 
@@ -543,12 +543,21 @@
             return
         # default case: produce the operation
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        # return self.emit(op)
+        return self.emit(op)
+
+    def postprocess_GETFIELD_GC_I(self, op):
         # then remember the result of reading the field
-        structinfo.setfield(descr, op.getarg(0), op, optheap=self, cf=cf)
+        structinfo = self.ensure_ptr_info_arg0(op)
+        cf = self.field_cache(op.getdescr())
+        structinfo.setfield(op.getdescr(), op.getarg(0), op, optheap=self,
+                            cf=cf)
     optimize_GETFIELD_GC_R = optimize_GETFIELD_GC_I
     optimize_GETFIELD_GC_F = optimize_GETFIELD_GC_I
 
+    postprocess_GETFIELD_GC_R = postprocess_GETFIELD_GC_I
+    postprocess_GETFIELD_GC_F = postprocess_GETFIELD_GC_I
+
     def optimize_SETFIELD_GC(self, op):
         self.setfield(op)
         #opnum = OpHelpers.getfield_pure_for_descr(op.getdescr())
@@ -582,9 +591,16 @@
                                          self.getintbound(op.getarg(1)))
         # default case: produce the operation
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        # return self.emit(op)
+        return self.emit(op)
+
+    def postprocess_GETARRAYITEM_GC_I(self, op):
         # then remember the result of reading the array item
-        if cf is not None:
+        arrayinfo = self.ensure_ptr_info_arg0(op)
+        indexb = self.getintbound(op.getarg(1))
+        if indexb.is_constant():
+            index = indexb.getint()
+            cf = self.arrayitem_cache(op.getdescr(), index)
             arrayinfo.setitem(op.getdescr(), indexb.getint(),
                               self.get_box_replacement(op.getarg(0)),
                               self.get_box_replacement(op), optheap=self,
@@ -592,6 +608,9 @@
     optimize_GETARRAYITEM_GC_R = optimize_GETARRAYITEM_GC_I
     optimize_GETARRAYITEM_GC_F = optimize_GETARRAYITEM_GC_I
 
+    postprocess_GETARRAYITEM_GC_R = postprocess_GETARRAYITEM_GC_I
+    postprocess_GETARRAYITEM_GC_F = postprocess_GETARRAYITEM_GC_I
+
     def optimize_GETARRAYITEM_GC_PURE_I(self, op):
         arrayinfo = self.ensure_ptr_info_arg0(op)
         indexb = self.getintbound(op.getarg(1))
@@ -610,7 +629,7 @@
             self.force_lazy_setarrayitem(op.getdescr(), 
self.getintbound(op.getarg(1)))
         # default case: produce the operation
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
 
     optimize_GETARRAYITEM_GC_PURE_R = optimize_GETARRAYITEM_GC_PURE_I
     optimize_GETARRAYITEM_GC_PURE_F = optimize_GETARRAYITEM_GC_PURE_I
@@ -634,7 +653,7 @@
             # variable index, so make sure the lazy setarrayitems are done
             self.force_lazy_setarrayitem(op.getdescr(), indexb, 
can_cache=False)
             # and then emit the operation
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_QUASIIMMUT_FIELD(self, op):
         # Pattern: QUASIIMMUT_FIELD(s, descr=QuasiImmutDescr)
@@ -672,9 +691,11 @@
         if self._seen_guard_not_invalidated:
             return
         self._seen_guard_not_invalidated = True
-        self.emit_operation(op)
+        return self.emit(op)
 
 
 dispatch_opt = make_dispatcher_method(OptHeap, 'optimize_',
-        default=OptHeap.emit_operation)
+                                      default=OptHeap.emit)
 OptHeap.propagate_forward = dispatch_opt
+dispatch_postprocess = make_dispatcher_method(OptHeap, 'postprocess_')
+OptHeap.propagate_postprocess = dispatch_postprocess
diff --git a/rpython/jit/metainterp/optimizeopt/info.py 
b/rpython/jit/metainterp/optimizeopt/info.py
--- a/rpython/jit/metainterp/optimizeopt/info.py
+++ b/rpython/jit/metainterp/optimizeopt/info.py
@@ -142,7 +142,7 @@
                 return constptr
             #
             op.set_forwarded(None)
-            optforce.emit_operation(op)
+            optforce.emit_extra(op)
             newop = optforce.getlastop()
             if newop is not op:
                 op.set_forwarded(newop)
@@ -220,7 +220,7 @@
                 setfieldop = ResOperation(rop.SETFIELD_GC, [op, subbox],
                                           descr=fielddescr)
                 self._fields[i] = None
-                optforce.emit_operation(setfieldop)
+                optforce.emit_extra(setfieldop)
 
     def _force_at_the_end_of_preamble(self, op, optforce, rec):
         if self._fields is None:
@@ -412,7 +412,7 @@
         # 'op = CALL_I(..., OS_RAW_MALLOC_VARSIZE_CHAR)'.
         # Emit now a CHECK_MEMORY_ERROR resop.
         check_op = ResOperation(rop.CHECK_MEMORY_ERROR, [op])
-        optforce.emit_operation(check_op)
+        optforce.emit_extra(check_op)
         #
         buffer = self._get_buffer()
         for i in range(len(buffer.offsets)):
@@ -422,7 +422,7 @@
             itembox = buffer.values[i]
             setfield_op = ResOperation(rop.RAW_STORE,
                               [op, ConstInt(offset), itembox], descr=descr)
-            optforce.emit_operation(setfield_op)
+            optforce.emit_extra(setfield_op)
 
     def _visitor_walk_recursive(self, op, visitor, optimizer):
         itemboxes = [optimizer.get_box_replacement(box)
@@ -537,7 +537,7 @@
                                  [op, ConstInt(i), subbox],
                                   descr=descr)
             self._items[i] = None
-            optforce.emit_operation(setop)
+            optforce.emit_extra(setop)
         optforce.pure_from_args(rop.ARRAYLEN_GC, [op], 
ConstInt(len(self._items)))
 
     def setitem(self, descr, index, struct, op, optheap=None, cf=None):
@@ -651,7 +651,7 @@
                     setfieldop = ResOperation(rop.SETINTERIORFIELD_GC,
                                               [op, ConstInt(index), subbox],
                                               descr=fielddescr)
-                    optforce.emit_operation(setfieldop)
+                    optforce.emit_extra(setfieldop)
                     # heapcache does not work for interiorfields
                     # if it does, we would need a fix here
                 i += 1
diff --git a/rpython/jit/metainterp/optimizeopt/intbounds.py 
b/rpython/jit/metainterp/optimizeopt/intbounds.py
--- a/rpython/jit/metainterp/optimizeopt/intbounds.py
+++ b/rpython/jit/metainterp/optimizeopt/intbounds.py
@@ -44,11 +44,10 @@
        redundant guards"""
 
     def propagate_forward(self, op):
-        dispatch_opt(self, op)
+        return dispatch_opt(self, op)
 
-    def opt_default(self, op):
-        assert not op.is_ovf()
-        self.emit_operation(op)
+    def propagate_postprocess(self, op):
+        return dispatch_postprocess(self, op)
 
     def propagate_bounds_backward(self, box):
         # FIXME: This takes care of the instruction where box is the reuslt
@@ -62,7 +61,9 @@
             dispatch_bounds_ops(self, box)
 
     def _optimize_guard_true_false_value(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def _postprocess_guard_true_false_value(self, op):
         if op.getarg(0).type == 'i':
             self.propagate_bounds_backward(op.getarg(0))
 
@@ -70,18 +71,26 @@
     optimize_GUARD_FALSE = _optimize_guard_true_false_value
     optimize_GUARD_VALUE = _optimize_guard_true_false_value
 
+    postprocess_GUARD_TRUE = _postprocess_guard_true_false_value
+    postprocess_GUARD_FALSE = _postprocess_guard_true_false_value
+    postprocess_GUARD_VALUE = _postprocess_guard_true_false_value
+
     def optimize_INT_OR_or_XOR(self, op):
         v1 = self.get_box_replacement(op.getarg(0))
-        b1 = self.getintbound(v1)
         v2 = self.get_box_replacement(op.getarg(1))
-        b2 = self.getintbound(v2)
         if v1 is v2:
             if op.getopnum() == rop.INT_OR:
                 self.make_equal_to(op, v1)
             else:
                 self.make_constant_int(op, 0)
-            return
-        self.emit_operation(op)
+            return None
+        return self.emit(op)
+
+    def postprocess_INT_OR_or_XOR(self, op):
+        v1 = self.get_box_replacement(op.getarg(0))
+        b1 = self.getintbound(v1)
+        v2 = self.get_box_replacement(op.getarg(1))
+        b2 = self.getintbound(v2)
         if b1.known_ge(IntBound(0, 0)) and \
            b2.known_ge(IntBound(0, 0)):
             r = self.getintbound(op)
@@ -91,11 +100,15 @@
     optimize_INT_OR = optimize_INT_OR_or_XOR
     optimize_INT_XOR = optimize_INT_OR_or_XOR
 
+    postprocess_INT_OR = postprocess_INT_OR_or_XOR
+    postprocess_INT_XOR = postprocess_INT_OR_or_XOR
+
     def optimize_INT_AND(self, op):
+        return self.emit(op)
+
+    def postprocess_INT_AND(self, op):
         b1 = self.getintbound(op.getarg(0))
         b2 = self.getintbound(op.getarg(1))
-        self.emit_operation(op)
-
         r = self.getintbound(op)
         pos1 = b1.known_ge(IntBound(0, 0))
         pos2 = b2.known_ge(IntBound(0, 0))
@@ -107,7 +120,9 @@
             r.make_le(b2)
 
     def optimize_INT_SUB(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_INT_SUB(self, op):
         b1 = self.getintbound(op.getarg(0))
         b2 = self.getintbound(op.getarg(1))
         b = b1.sub_bound(b2)
@@ -118,8 +133,7 @@
         arg1 = self.get_box_replacement(op.getarg(0))
         arg2 = self.get_box_replacement(op.getarg(1))
         if self.is_raw_ptr(arg1) or self.is_raw_ptr(arg2):
-            self.emit_operation(op)
-            return
+            return self.emit(op)
         v1 = self.getintbound(arg1)
         v2 = self.getintbound(arg2)
 
@@ -153,7 +167,9 @@
                         arg2 = ConstInt(sum)
                         op = self.replace_op_with(op, rop.INT_ADD, args=[arg1, 
arg2])
 
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_INT_ADD(self, op):
         b1 = self.getintbound(op.getarg(0))
         b2 = self.getintbound(op.getarg(1))
         r = self.getintbound(op)
@@ -162,38 +178,37 @@
             r.intersect(b)
 
     def optimize_INT_MUL(self, op):
+        return self.emit(op)
+
+    def postprocess_INT_MUL(self, op):
         b1 = self.getintbound(op.getarg(0))
         b2 = self.getintbound(op.getarg(1))
-        self.emit_operation(op)
         r = self.getintbound(op)
         b = b1.mul_bound(b2)
         if b.bounded():
             r.intersect(b)
 
     def optimize_CALL_PURE_I(self, op):
+        return self.emit(op)
+
+    def postprocess_CALL_PURE_I(self, op):
         # dispatch based on 'oopspecindex' to a method that handles
         # specifically the given oopspec call.
         effectinfo = op.getdescr().get_extra_info()
         oopspecindex = effectinfo.oopspecindex
         if oopspecindex == EffectInfo.OS_INT_PY_DIV:
-            self.opt_call_INT_PY_DIV(op)
-            return
+            self.post_call_INT_PY_DIV(op)
         elif oopspecindex == EffectInfo.OS_INT_PY_MOD:
-            self.opt_call_INT_PY_MOD(op)
-            return
-        self.emit_operation(op)
+            self.post_call_INT_PY_MOD(op)
 
-    def opt_call_INT_PY_DIV(self, op):
+    def post_call_INT_PY_DIV(self, op):
         b1 = self.getintbound(op.getarg(1))
         b2 = self.getintbound(op.getarg(2))
-        self.emit_operation(op)
         r = self.getintbound(op)
         r.intersect(b1.py_div_bound(b2))
 
-    def opt_call_INT_PY_MOD(self, op):
-        b1 = self.getintbound(op.getarg(1))
+    def post_call_INT_PY_MOD(self, op):
         b2 = self.getintbound(op.getarg(2))
-        self.emit_operation(op)
         if b2.is_constant():
             val = b2.getint()
             r = self.getintbound(op)
@@ -205,11 +220,13 @@
                 r.make_le(IntBound(0, 0))
 
     def optimize_INT_LSHIFT(self, op):
+        return self.emit(op)
+
+    def postprocess_INT_LSHIFT(self, op):
         arg0 = self.get_box_replacement(op.getarg(0))
         b1 = self.getintbound(arg0)
         arg1 = self.get_box_replacement(op.getarg(1))
         b2 = self.getintbound(arg1)
-        self.emit_operation(op)
         r = self.getintbound(op)
         b = b1.lshift_bound(b2)
         r.intersect(b)
@@ -228,10 +245,15 @@
         if b.has_lower and b.has_upper and b.lower == b.upper:
             # constant result (likely 0, for rshifts that kill all bits)
             self.make_constant_int(op, b.lower)
-        else:
-            self.emit_operation(op)
-            r = self.getintbound(op)
-            r.intersect(b)
+            return None
+        return self.emit(op)
+
+    def postprocess_INT_RSHIFT(self, op):
+        b1 = self.getintbound(op.getarg(0))
+        b2 = self.getintbound(op.getarg(1))
+        b = b1.rshift_bound(b2)
+        r = self.getintbound(op)
+        r.intersect(b)
 
     def optimize_GUARD_NO_OVERFLOW(self, op):
         lastop = self.last_emitted_operation
@@ -257,7 +279,7 @@
                 self.pure_from_args(rop.INT_SUB, [args[0], result], args[1])
             #elif opnum == rop.INT_MUL_OVF:
             #    self.pure(rop.INT_MUL, args[:], result)
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_GUARD_OVERFLOW(self, op):
         # If INT_xxx_OVF was replaced by INT_xxx, *but* we still see
@@ -270,7 +292,7 @@
             raise InvalidLoop('An INT_xxx_OVF was proven not to overflow but' +
                               'guarded with GUARD_OVERFLOW')
 
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_INT_ADD_OVF(self, op):
         b1 = self.getintbound(op.getarg(0))
@@ -281,7 +303,12 @@
             # by optimize_GUARD_NO_OVERFLOW; if we see instead an
             # optimize_GUARD_OVERFLOW, then InvalidLoop.
             op = self.replace_op_with(op, rop.INT_ADD)
-        self.emit_operation(op) # emit the op
+        return self.emit(op)
+
+    def postprocess_INT_ADD_OVF(self, op):
+        b1 = self.getintbound(op.getarg(0))
+        b2 = self.getintbound(op.getarg(1))
+        resbound = b1.add_bound(b2)
         r = self.getintbound(op)
         r.intersect(resbound)
 
@@ -292,11 +319,18 @@
         b1 = self.getintbound(arg1)
         if arg0.same_box(arg1):
             self.make_constant_int(op, 0)
-            return
+            return None
         resbound = b0.sub_bound(b1)
         if resbound.bounded():
             op = self.replace_op_with(op, rop.INT_SUB)
-        self.emit_operation(op) # emit the op
+        return self.emit(op)
+
+    def postprocess_INT_SUB_OVF(self, op):
+        arg0 = self.get_box_replacement(op.getarg(0))
+        arg1 = self.get_box_replacement(op.getarg(1))
+        b0 = self.getintbound(arg0)
+        b1 = self.getintbound(arg1)
+        resbound = b0.sub_bound(b1)
         r = self.getintbound(op)
         r.intersect(resbound)
 
@@ -306,7 +340,12 @@
         resbound = b1.mul_bound(b2)
         if resbound.bounded():
             op = self.replace_op_with(op, rop.INT_MUL)
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_INT_MUL_OVF(self, op):
+        b1 = self.getintbound(op.getarg(0))
+        b2 = self.getintbound(op.getarg(1))
+        resbound = b1.mul_bound(b2)
         r = self.getintbound(op)
         r.intersect(resbound)
 
@@ -320,7 +359,7 @@
         elif b1.known_ge(b2) or arg1 is arg2:
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_GT(self, op):
         arg1 = self.get_box_replacement(op.getarg(0))
@@ -332,7 +371,7 @@
         elif b1.known_le(b2) or arg1 is arg2:
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_LE(self, op):
         arg1 = self.get_box_replacement(op.getarg(0))
@@ -344,7 +383,7 @@
         elif b1.known_gt(b2):
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_GE(self, op):
         arg1 = self.get_box_replacement(op.getarg(0))
@@ -356,7 +395,7 @@
         elif b1.known_lt(b2):
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_EQ(self, op):
         arg0 = self.get_box_replacement(op.getarg(0))
@@ -370,7 +409,7 @@
         elif arg0.same_box(arg1):
             self.make_constant_int(op, 1)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_NE(self, op):
         arg0 = self.get_box_replacement(op.getarg(0))
@@ -384,14 +423,14 @@
         elif arg0 is arg1:
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_FORCE_GE_ZERO(self, op):
         b = self.getintbound(op.getarg(0))
         if b.known_ge(IntBound(0, 0)):
             self.make_equal_to(op, op.getarg(0))
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_SIGNEXT(self, op):
         b = self.getintbound(op.getarg(0))
@@ -402,29 +441,43 @@
         if bounds.contains_bound(b):
             self.make_equal_to(op, op.getarg(0))
         else:
-            self.emit_operation(op)
-            bres = self.getintbound(op)
-            bres.intersect(bounds)
+            return self.emit(op)
+
+    def postprocess_INT_SIGNEXT(self, op):
+        numbits = op.getarg(1).getint() * 8
+        start = -(1 << (numbits - 1))
+        stop = 1 << (numbits - 1)
+        bounds = IntBound(start, stop - 1)
+        bres = self.getintbound(op)
+        bres.intersect(bounds)
 
     def optimize_ARRAYLEN_GC(self, op):
+        return self.emit(op)
+
+    def postprocess_ARRAYLEN_GC(self, op):
         array = self.ensure_ptr_info_arg0(op)
-        self.emit_operation(op)
         self.optimizer.setintbound(op, array.getlenbound(None))
 
     def optimize_STRLEN(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_STRLEN(self, op):
         self.make_nonnull_str(op.getarg(0), vstring.mode_string)
         array = self.getptrinfo(op.getarg(0))
         self.optimizer.setintbound(op, array.getlenbound(vstring.mode_string))
 
     def optimize_UNICODELEN(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_UNICODELEN(self, op):
         self.make_nonnull_str(op.getarg(0), vstring.mode_unicode)
         array = self.getptrinfo(op.getarg(0))
         self.optimizer.setintbound(op, array.getlenbound(vstring.mode_unicode))
 
     def optimize_STRGETITEM(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_STRGETITEM(self, op):
         v1 = self.getintbound(op)
         v2 = self.getptrinfo(op.getarg(0))
         intbound = self.getintbound(op.getarg(1))
@@ -436,7 +489,9 @@
         v1.make_lt(IntUpperBound(256))
 
     def optimize_GETFIELD_RAW_I(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_GETFIELD_RAW_I(self, op):
         descr = op.getdescr()
         if descr.is_integer_bounded():
             b1 = self.getintbound(op)
@@ -449,12 +504,24 @@
     optimize_GETFIELD_GC_R = optimize_GETFIELD_RAW_I
     optimize_GETFIELD_GC_F = optimize_GETFIELD_RAW_I
 
+    postprocess_GETFIELD_RAW_F = postprocess_GETFIELD_RAW_I
+    postprocess_GETFIELD_RAW_R = postprocess_GETFIELD_RAW_I
+    postprocess_GETFIELD_GC_I = postprocess_GETFIELD_RAW_I
+    postprocess_GETFIELD_GC_R = postprocess_GETFIELD_RAW_I
+    postprocess_GETFIELD_GC_F = postprocess_GETFIELD_RAW_I
+
     optimize_GETINTERIORFIELD_GC_I = optimize_GETFIELD_RAW_I
     optimize_GETINTERIORFIELD_GC_R = optimize_GETFIELD_RAW_I
     optimize_GETINTERIORFIELD_GC_F = optimize_GETFIELD_RAW_I
 
+    postprocess_GETINTERIORFIELD_GC_I = postprocess_GETFIELD_RAW_I
+    postprocess_GETINTERIORFIELD_GC_R = postprocess_GETFIELD_RAW_I
+    postprocess_GETINTERIORFIELD_GC_F = postprocess_GETFIELD_RAW_I
+
     def optimize_GETARRAYITEM_RAW_I(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_GETARRAYITEM_RAW_I(self, op):
         descr = op.getdescr()
         if descr and descr.is_item_integer_bounded():
             intbound = self.getintbound(op)
@@ -466,8 +533,15 @@
     optimize_GETARRAYITEM_GC_F = optimize_GETARRAYITEM_RAW_I
     optimize_GETARRAYITEM_GC_R = optimize_GETARRAYITEM_RAW_I
 
+    postprocess_GETARRAYITEM_RAW_F = postprocess_GETARRAYITEM_RAW_I
+    postprocess_GETARRAYITEM_GC_I = postprocess_GETARRAYITEM_RAW_I
+    postprocess_GETARRAYITEM_GC_F = postprocess_GETARRAYITEM_RAW_I
+    postprocess_GETARRAYITEM_GC_R = postprocess_GETARRAYITEM_RAW_I
+
     def optimize_UNICODEGETITEM(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_UNICODEGETITEM(self, op):
         b1 = self.getintbound(op)
         b1.make_ge(IntLowerBound(0))
         v2 = self.getptrinfo(op.getarg(0))
@@ -632,5 +706,6 @@
 
 
 dispatch_opt = make_dispatcher_method(OptIntBounds, 'optimize_',
-        default=OptIntBounds.opt_default)
+                                      default=OptIntBounds.emit)
 dispatch_bounds_ops = make_dispatcher_method(OptIntBounds, 'propagate_bounds_')
+dispatch_postprocess = make_dispatcher_method(OptIntBounds, 'postprocess_')
diff --git a/rpython/jit/metainterp/optimizeopt/optimizer.py 
b/rpython/jit/metainterp/optimizeopt/optimizer.py
--- a/rpython/jit/metainterp/optimizeopt/optimizer.py
+++ b/rpython/jit/metainterp/optimizeopt/optimizer.py
@@ -41,6 +41,15 @@
         pass
 
 
+class OptimizationResult(object):
+    def __init__(self, opt, op):
+        self.opt = opt
+        self.op = op
+
+    def callback(self):
+        self.opt.propagate_postprocess(self.op)
+
+
 class Optimization(object):
     next_optimization = None
     potential_extra_ops = None
@@ -48,15 +57,29 @@
     def __init__(self):
         pass # make rpython happy
 
-    def send_extra_operation(self, op):
-        self.optimizer.send_extra_operation(op)
+    def send_extra_operation(self, op, opt=None):
+        self.optimizer.send_extra_operation(op, opt)
 
     def propagate_forward(self, op):
         raise NotImplementedError
 
+    def propagate_postprocess(self, op):
+        pass
+
     def emit_operation(self, op):
-        self.last_emitted_operation = op
-        self.next_optimization.propagate_forward(op)
+        assert False, "This should never be called."
+
+    def emit(self, op):
+        return self.emit_result(OptimizationResult(self, op))
+
+    def emit_result(self, opt_result):
+        self.last_emitted_operation = opt_result.op
+        return opt_result
+
+    def emit_extra(self, op, emit=True):
+        if emit:
+            self.emit(op)
+        self.send_extra_operation(op, self.next_optimization)
 
     def getintbound(self, op):
         assert op.type == 'i'
@@ -290,7 +313,7 @@
             optimizations = []
             self.first_optimization = self
 
-        self.optimizations  = optimizations
+        self.optimizations = optimizations
 
     def force_op_from_preamble(self, op):
         return op
@@ -524,7 +547,7 @@
             if op.getopnum() in (rop.FINISH, rop.JUMP):
                 last_op = op
                 break
-            self.first_optimization.propagate_forward(op)
+            self.send_extra_operation(op)
             trace.kill_cache_at(deadranges[i + trace.start_index])
             if op.type != 'v':
                 i += 1
@@ -532,9 +555,9 @@
         if flush:
             self.flush()
             if last_op:
-                self.first_optimization.propagate_forward(last_op)
+                self.send_extra_operation(last_op)
         self.resumedata_memo.update_counters(self.metainterp_sd.profiler)
-        
+
         return (BasicLoopInfo(trace.inputargs, self.quasi_immutable_deps, 
last_op),
                 self._newoperations)
 
@@ -543,13 +566,30 @@
             if op.get_forwarded() is not None:
                 op.set_forwarded(None)
 
-    def send_extra_operation(self, op):
-        self.first_optimization.propagate_forward(op)
+    def send_extra_operation(self, op, opt=None):
+        if opt is None:
+            opt = self.first_optimization
+        opt_results = []
+        while opt is not None:
+            opt_result = opt.propagate_forward(op)
+            if opt_result is None:
+                op = None
+                break
+            opt_results.append(opt_result)
+            op = opt_result.op
+            opt = opt.next_optimization
+        for opt_result in reversed(opt_results):
+            opt_result.callback()
 
     def propagate_forward(self, op):
         dispatch_opt(self, op)
 
-    def emit_operation(self, op):
+    def emit_extra(self, op, emit=True):
+        # no forwarding, because we're at the end of the chain
+        self.emit(op)
+
+    def emit(self, op):
+        # this actually emits the operation instead of forwarding it
         if rop.returns_bool_result(op.opnum):
             self.getintbound(op).make_bool()
         self._emit_operation(op)
@@ -740,7 +780,7 @@
         return op
 
     def optimize_default(self, op):
-        self.emit_operation(op)
+        self.emit(op)
 
     def constant_fold(self, op):
         self.protect_speculative_operation(op)
@@ -885,14 +925,14 @@
     #def optimize_GUARD_NO_OVERFLOW(self, op):
     #    # otherwise the default optimizer will clear fields, which is unwanted
     #    # in this case
-    #    self.emit_operation(op)
+    #    self.emit(op)
     # FIXME: Is this still needed?
 
     def optimize_DEBUG_MERGE_POINT(self, op):
-        self.emit_operation(op)
+        self.emit(op)
 
     def optimize_JIT_DEBUG(self, op):
-        self.emit_operation(op)
+        self.emit(op)
 
     def optimize_STRGETITEM(self, op):
         indexb = self.getintbound(op.getarg(1))
diff --git a/rpython/jit/metainterp/optimizeopt/pure.py 
b/rpython/jit/metainterp/optimizeopt/pure.py
--- a/rpython/jit/metainterp/optimizeopt/pure.py
+++ b/rpython/jit/metainterp/optimizeopt/pure.py
@@ -1,4 +1,5 @@
-from rpython.jit.metainterp.optimizeopt.optimizer import Optimization, REMOVED
+from rpython.jit.metainterp.optimizeopt.optimizer import (
+    Optimization, OptimizationResult, REMOVED)
 from rpython.jit.metainterp.resoperation import rop, OpHelpers, AbstractResOp,\
      ResOperation
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
@@ -6,6 +7,31 @@
 from rpython.jit.metainterp.optimize import SpeculativeError
 
 
+class DefaultOptimizationResult(OptimizationResult):
+    def __init__(self, opt, op, save, nextop):
+        OptimizationResult.__init__(self, opt, op)
+        self.save = save
+        self.nextop = nextop
+
+    def callback(self):
+        self._callback(self.op, self.save, self.nextop)
+
+    def _callback(self, op, save, nextop):
+        if rop.returns_bool_result(op.opnum):
+            self.opt.getintbound(op).make_bool()
+        if save:
+            recentops = self.opt.getrecentops(op.getopnum())
+            recentops.add(op)
+        if nextop:
+            self.opt.emit_extra(nextop)
+
+
+class CallPureOptimizationResult(OptimizationResult):
+    def callback(self):
+        self.opt.call_pure_positions.append(
+            len(self.opt.optimizer._newoperations) - 1)
+
+
 class RecentPureOps(object):
     REMEMBER_LIMIT = 16
 
@@ -72,7 +98,10 @@
         self.extra_call_pure = []
 
     def propagate_forward(self, op):
-        dispatch_opt(self, op)
+        return dispatch_opt(self, op)
+
+    def propagate_postprocess(self, op):
+        dispatch_postprocess(self, op)
 
     def optimize_default(self, op):
         canfold = rop.is_always_pure(op.opnum)
@@ -109,14 +138,7 @@
                 return
 
         # otherwise, the operation remains
-        self.emit_operation(op)
-        if rop.returns_bool_result(op.opnum):
-            self.getintbound(op).make_bool()
-        if save:
-            recentops = self.getrecentops(op.getopnum())
-            recentops.add(op)
-        if nextop:
-            self.emit_operation(nextop)
+        return self.emit_result(DefaultOptimizationResult(self, op, save, 
nextop))
 
     def getrecentops(self, opnum):
         if rop._OVF_FIRST <= opnum <= rop._OVF_LAST:
@@ -159,9 +181,7 @@
         # replace CALL_PURE with just CALL
         opnum = OpHelpers.call_for_descr(op.getdescr())
         newop = self.optimizer.replace_op_with(op, opnum)
-        self.emit_operation(newop)
-        self.call_pure_positions.append(
-            len(self.optimizer._newoperations) - 1)
+        return self.emit_result(CallPureOptimizationResult(self, newop))
 
     optimize_CALL_PURE_R = optimize_CALL_PURE_I
     optimize_CALL_PURE_F = optimize_CALL_PURE_I
@@ -191,7 +211,7 @@
             # it was a CALL_PURE that was killed; so we also kill the
             # following GUARD_NO_EXCEPTION
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def flush(self):
         assert self.postponed_op is None
@@ -237,3 +257,4 @@
 
 dispatch_opt = make_dispatcher_method(OptPure, 'optimize_',
                                       default=OptPure.optimize_default)
+dispatch_postprocess = make_dispatcher_method(OptPure, 'postprocess_')
diff --git a/rpython/jit/metainterp/optimizeopt/rewrite.py 
b/rpython/jit/metainterp/optimizeopt/rewrite.py
--- a/rpython/jit/metainterp/optimizeopt/rewrite.py
+++ b/rpython/jit/metainterp/optimizeopt/rewrite.py
@@ -5,8 +5,8 @@
                                             ConstFloat)
 from rpython.jit.metainterp.optimize import InvalidLoop
 from rpython.jit.metainterp.optimizeopt.intutils import IntBound
-from rpython.jit.metainterp.optimizeopt.optimizer import (Optimization, 
REMOVED,
-    CONST_0, CONST_1)
+from rpython.jit.metainterp.optimizeopt.optimizer import (
+    Optimization, OptimizationResult, REMOVED, CONST_0, CONST_1)
 from rpython.jit.metainterp.optimizeopt.info import INFO_NONNULL, INFO_NULL
 from rpython.jit.metainterp.optimizeopt.util import _findall, 
make_dispatcher_method
 from rpython.jit.metainterp.resoperation import rop, ResOperation, opclasses,\
@@ -16,6 +16,21 @@
 from rpython.rtyper import rclass
 import math
 
+
+class CallLoopinvariantOptimizationResult(OptimizationResult):
+    def __init__(self, opt, op, old_op):
+        OptimizationResult.__init__(self, opt, op)
+        self.old_op = old_op
+
+    def callback(self):
+        self._callback(self.op, self.old_op)
+
+    def _callback(self, op, old_op):
+        key = make_hashable_int(op.getarg(0).getint())
+        self.opt.loop_invariant_producer[key] = self.opt.optimizer.getlastop()
+        self.opt.loop_invariant_results[key] = old_op
+
+
 class OptRewrite(Optimization):
     """Rewrite operations into equivalent, cheaper operations.
        This includes already executed operations and constants.
@@ -36,7 +51,10 @@
             if self.find_rewritable_bool(op):
                 return
 
-        dispatch_opt(self, op)
+        return dispatch_opt(self, op)
+
+    def propagate_postprocess(self, op):
+        return dispatch_postprocess(self, op)
 
     def try_boolinvers(self, op, targs):
         oldop = self.get_pure_result(targs)
@@ -97,7 +115,7 @@
                 self.make_equal_to(op, op.getarg(1))
                 return
 
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_INT_OR(self, op):
         b1 = self.getintbound(op.getarg(0))
@@ -107,7 +125,7 @@
         elif b2.equal(0):
             self.make_equal_to(op, op.getarg(0))
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_SUB(self, op):
         arg1 = self.get_box_replacement(op.getarg(0))
@@ -118,17 +136,18 @@
             self.make_equal_to(op, arg1)
         elif b1.equal(0):
             op = self.replace_op_with(op, rop.INT_NEG, args=[arg2])
-            self.emit_operation(op)
+            return self.emit(op)
         elif arg1 == arg2:
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
-            self.optimizer.pure_reverse(op)
+            return self.emit(op)
+
+    def postprocess_INT_SUB(self, op):
+        self.optimizer.pure_reverse(op)
 
     def optimize_INT_ADD(self, op):
         if self.is_raw_ptr(op.getarg(0)) or self.is_raw_ptr(op.getarg(1)):
-            self.emit_operation(op)
-            return
+            return self.emit(op)
         arg1 = self.get_box_replacement(op.getarg(0))
         b1 = self.getintbound(arg1)
         arg2 = self.get_box_replacement(op.getarg(1))
@@ -140,8 +159,10 @@
         elif b2.equal(0):
             self.make_equal_to(op, arg1)
         else:
-            self.emit_operation(op)
-            self.optimizer.pure_reverse(op)
+            return self.emit(op)
+
+    def postprocess_INT_ADD(self, op):
+        self.optimizer.pure_reverse(op)
 
     def optimize_INT_MUL(self, op):
         arg1 = self.get_box_replacement(op.getarg(0))
@@ -166,7 +187,7 @@
                         new_rhs = ConstInt(highest_bit(lh_info.getint()))
                         op = self.replace_op_with(op, rop.INT_LSHIFT, 
args=[rhs, new_rhs])
                         break
-            self.emit_operation(op)
+            return self.emit(op)
 
     def _optimize_CALL_INT_UDIV(self, op):
         b2 = self.getintbound(op.getarg(2))
@@ -185,7 +206,7 @@
         elif b1.is_constant() and b1.getint() == 0:
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_RSHIFT(self, op):
         b1 = self.getintbound(op.getarg(0))
@@ -196,7 +217,7 @@
         elif b1.is_constant() and b1.getint() == 0:
             self.make_constant_int(op, 0)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_XOR(self, op):
         b1 = self.getintbound(op.getarg(0))
@@ -207,7 +228,7 @@
         elif b2.equal(0):
             self.make_equal_to(op, op.getarg(0))
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_FLOAT_MUL(self, op):
         arg1 = op.getarg(0)
@@ -225,9 +246,10 @@
                     return
                 elif v1.getfloat() == -1.0:
                     newop = self.replace_op_with(op, rop.FLOAT_NEG, args=[rhs])
-                    self.emit_operation(newop)
-                    return
-        self.emit_operation(op)
+                    return self.emit(newop)
+        return self.emit(op)
+
+    def postprocess_FLOAT_MUL(self, op):
         self.optimizer.pure_reverse(op)
 
     def optimize_FLOAT_TRUEDIV(self, op):
@@ -249,13 +271,15 @@
                     c = ConstFloat(longlong.getfloatstorage(reciprocal))
                     newop = self.replace_op_with(op, rop.FLOAT_MUL,
                                                  args=[arg1, c])
-        self.emit_operation(newop)
+        return self.emit(newop)
 
     def optimize_FLOAT_NEG(self, op):
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_FLOAT_NEG(self, op):
         self.optimizer.pure_reverse(op)
 
-    def optimize_guard(self, op, constbox, emit_operation=True):
+    def optimize_guard(self, op, constbox):
         box = op.getarg(0)
         if box.type == 'i':
             intbound = self.getintbound(box)
@@ -275,12 +299,8 @@
                     raise InvalidLoop('A GUARD_VALUE (%s) '
                                       'was proven to always fail' % r)
                 return
-                    
-        if emit_operation:
-            self.emit_operation(op)
-        self.make_constant(box, constbox)
-        #if self.optimizer.optheap:  XXX
-        #    self.optimizer.optheap.value_updated(value, 
self.getvalue(constbox))
+
+        return self.emit(op)
 
     def optimize_GUARD_ISNULL(self, op):
         info = self.getptrinfo(op.getarg(0))
@@ -291,7 +311,9 @@
                 r = self.optimizer.metainterp_sd.logger_ops.repr_of_resop(op)
                 raise InvalidLoop('A GUARD_ISNULL (%s) was proven to always '
                                   'fail' % r)
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_GUARD_ISNULL(self, op):
         self.make_constant(op.getarg(0), self.optimizer.cpu.ts.CONST_NULL)
 
     def optimize_GUARD_IS_OBJECT(self, op):
@@ -308,7 +330,7 @@
                 return
             if info.is_precise():
                 raise InvalidLoop()
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GUARD_GC_TYPE(self, op):
         info = self.getptrinfo(op.getarg(0))
@@ -322,7 +344,7 @@
             if info.get_descr().get_type_id() != op.getarg(1).getint():
                 raise InvalidLoop("wrong GC types passed around!")
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GUARD_SUBCLASS(self, op):
         info = self.getptrinfo(op.getarg(0))
@@ -343,7 +365,7 @@
                 if optimizer._check_subclass(info.get_descr().get_vtable(),
                                              op.getarg(1).getint()):
                     return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GUARD_NONNULL(self, op):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -354,7 +376,9 @@
                 r = self.optimizer.metainterp_sd.logger_ops.repr_of_resop(op)
                 raise InvalidLoop('A GUARD_NONNULL (%s) was proven to always '
                                   'fail' % r)
-        self.emit_operation(op)
+        return self.emit(op)
+
+    def postprocess_GUARD_NONNULL(self, op):
         self.make_nonnull(op.getarg(0))
         self.getptrinfo(op.getarg(0)).mark_last_guard(self.optimizer)
 
@@ -375,7 +399,11 @@
                 return
         constbox = op.getarg(1)
         assert isinstance(constbox, Const)
-        self.optimize_guard(op, constbox)
+        return self.optimize_guard(op, constbox)
+
+    def postprocess_GUARD_VALUE(self, op):
+        box = self.get_box_replacement(op.getarg(0))
+        self.make_constant(box, op.getarg(1))
 
     def replace_old_guard_with_guard_value(self, op, info, old_guard_op):
         # there already has been a guard_nonnull or guard_class or
@@ -418,10 +446,18 @@
         return op
 
     def optimize_GUARD_TRUE(self, op):
-        self.optimize_guard(op, CONST_1)
+        return self.optimize_guard(op, CONST_1)
+
+    def postprocess_GUARD_TRUE(self, op):
+        box = self.get_box_replacement(op.getarg(0))
+        self.make_constant(box, CONST_1)
 
     def optimize_GUARD_FALSE(self, op):
-        self.optimize_guard(op, CONST_0)
+        return self.optimize_guard(op, CONST_0)
+
+    def postprocess_GUARD_FALSE(self, op):
+        box = self.get_box_replacement(op.getarg(0))
+        self.make_constant(box, CONST_0)
 
     def optimize_RECORD_EXACT_CLASS(self, op):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -464,11 +500,16 @@
                 # not put in short preambles guard_nonnull and guard_class
                 # on the same box.
                 self.optimizer.replace_guard(op, info)
-                self.emit_operation(op)
-                self.make_constant_class(op.getarg(0), expectedclassbox, False)
-                return
-        self.emit_operation(op)
-        self.make_constant_class(op.getarg(0), expectedclassbox)
+                return self.emit(op)
+        return self.emit(op)
+
+    def postprocess_GUARD_CLASS(self, op):
+        expectedclassbox = op.getarg(1)
+        info = self.getptrinfo(op.getarg(0))
+        old_guard_op = info.get_last_guard(self.optimizer)
+        update_last_guard = not old_guard_op or isinstance(
+            old_guard_op.getdescr(), compile.ResumeAtPositionDescr)
+        self.make_constant_class(op.getarg(0), expectedclassbox, 
update_last_guard)
 
     def optimize_GUARD_NONNULL_CLASS(self, op):
         info = self.getptrinfo(op.getarg(0))
@@ -476,7 +517,9 @@
             r = self.optimizer.metainterp_sd.logger_ops.repr_of_resop(op)
             raise InvalidLoop('A GUARD_NONNULL_CLASS (%s) was proven to '
                               'always fail' % r)
-        self.optimize_GUARD_CLASS(op)
+        return self.optimize_GUARD_CLASS(op)
+
+    postprocess_GUARD_NONNULL_CLASS = postprocess_GUARD_CLASS
 
     def optimize_CALL_LOOPINVARIANT_I(self, op):
         arg = op.getarg(0)
@@ -496,9 +539,8 @@
         # there is no reason to have a separate operation for this
         newop = self.replace_op_with(op,
                                      OpHelpers.call_for_descr(op.getdescr()))
-        self.emit_operation(newop)
-        self.loop_invariant_producer[key] = self.optimizer.getlastop()
-        self.loop_invariant_results[key] = op
+        return self.emit_result(CallLoopinvariantOptimizationResult(self, 
newop, op))
+
     optimize_CALL_LOOPINVARIANT_R = optimize_CALL_LOOPINVARIANT_I
     optimize_CALL_LOOPINVARIANT_F = optimize_CALL_LOOPINVARIANT_I
     optimize_CALL_LOOPINVARIANT_N = optimize_CALL_LOOPINVARIANT_I
@@ -512,7 +554,7 @@
                 return
             opnum = OpHelpers.call_for_type(op.type)
             op = op.copy_and_change(opnum, args=op.getarglist()[1:])
-        self.emit_operation(op)
+        return self.emit(op)
 
     def _optimize_nullness(self, op, box, expect_nonnull):
         info = self.getnullness(box)
@@ -521,17 +563,17 @@
         elif info == INFO_NULL:
             self.make_constant_int(op, not expect_nonnull)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_INT_IS_TRUE(self, op):
         if (not self.is_raw_ptr(op.getarg(0)) and
             self.getintbound(op.getarg(0)).is_bool()):
             self.make_equal_to(op, op.getarg(0))
             return
-        self._optimize_nullness(op, op.getarg(0), True)
+        return self._optimize_nullness(op, op.getarg(0), True)
 
     def optimize_INT_IS_ZERO(self, op):
-        self._optimize_nullness(op, op.getarg(0), False)
+        return self._optimize_nullness(op, op.getarg(0), False)
 
     def _optimize_oois_ooisnot(self, op, expect_isnot, instance):
         arg0 = self.get_box_replacement(op.getarg(0))
@@ -547,9 +589,9 @@
         elif info1 and info1.is_virtual():
             self.make_constant_int(op, expect_isnot)
         elif info1 and info1.is_null():
-            self._optimize_nullness(op, op.getarg(0), expect_isnot)
+            return self._optimize_nullness(op, op.getarg(0), expect_isnot)
         elif info0 and info0.is_null():
-            self._optimize_nullness(op, op.getarg(1), expect_isnot)
+            return self._optimize_nullness(op, op.getarg(1), expect_isnot)
         elif arg0 is arg1:
             self.make_constant_int(op, not expect_isnot)
         else:
@@ -568,19 +610,19 @@
                         # class is different
                         self.make_constant_int(op, expect_isnot)
                         return
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_PTR_EQ(self, op):
-        self._optimize_oois_ooisnot(op, False, False)
+        return self._optimize_oois_ooisnot(op, False, False)
 
     def optimize_PTR_NE(self, op):
-        self._optimize_oois_ooisnot(op, True, False)
+        return self._optimize_oois_ooisnot(op, True, False)
 
     def optimize_INSTANCE_PTR_EQ(self, op):
-        self._optimize_oois_ooisnot(op, False, True)
+        return self._optimize_oois_ooisnot(op, False, True)
 
     def optimize_INSTANCE_PTR_NE(self, op):
-        self._optimize_oois_ooisnot(op, True, True)
+        return self._optimize_oois_ooisnot(op, True, True)
 
     def optimize_CALL_N(self, op):
         # dispatch based on 'oopspecindex' to a method that handles
@@ -589,14 +631,13 @@
         effectinfo = op.getdescr().get_extra_info()
         oopspecindex = effectinfo.oopspecindex
         if oopspecindex == EffectInfo.OS_ARRAYCOPY:
-            if self._optimize_CALL_ARRAYCOPY(op):
-                return
-        self.emit_operation(op)
+            return self._optimize_CALL_ARRAYCOPY(op)
+        return self.emit(op)
 
     def _optimize_CALL_ARRAYCOPY(self, op):
         length = self.get_constant_box(op.getarg(5))
         if length and length.getint() == 0:
-            return True # 0-length arraycopy
+            return None  # 0-length arraycopy
 
         source_info = self.getptrinfo(op.getarg(1))
         dest_info = self.getptrinfo(op.getarg(2))
@@ -612,7 +653,7 @@
             dest_start = dest_start_box.getint()
             arraydescr = extrainfo.single_write_descr_array
             if arraydescr.is_array_of_structs():
-                return False       # not supported right now
+                return self.emit(op)       # not supported right now
 
             # XXX fish fish fish
             for index in range(length.getint()):
@@ -638,9 +679,9 @@
                                           ConstInt(index + dest_start),
                                           val],
                                          descr=arraydescr)
-                    self.emit_operation(newop)
-            return True
-        return False
+                    self.optimizer.send_extra_operation(newop)
+            return None
+        return self.emit(op)
 
     def optimize_CALL_PURE_I(self, op):
         # this removes a CALL_PURE with all constant arguments.
@@ -650,6 +691,7 @@
             self.make_constant(op, result)
             self.last_emitted_operation = REMOVED
             return
+
         # dispatch based on 'oopspecindex' to a method that handles
         # specifically the given oopspec call.
         effectinfo = op.getdescr().get_extra_info()
@@ -663,7 +705,7 @@
         elif oopspecindex == EffectInfo.OS_INT_PY_MOD:
             if self._optimize_CALL_INT_PY_MOD(op):
                 return
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_CALL_PURE_R = optimize_CALL_PURE_I
     optimize_CALL_PURE_F = optimize_CALL_PURE_I
     optimize_CALL_PURE_N = optimize_CALL_PURE_I
@@ -673,7 +715,7 @@
             # it was a CALL_PURE or a CALL_LOOPINVARIANT that was killed;
             # so we also kill the following GUARD_NO_EXCEPTION
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GUARD_FUTURE_CONDITION(self, op):
         self.optimizer.notice_guard_future_condition(op)
@@ -758,11 +800,11 @@
 
     def optimize_CAST_PTR_TO_INT(self, op):
         self.optimizer.pure_reverse(op)
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_CAST_INT_TO_PTR(self, op):
         self.optimizer.pure_reverse(op)
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_SAME_AS_I(self, op):
         self.make_equal_to(op, op.getarg(0))
@@ -770,5 +812,6 @@
     optimize_SAME_AS_F = optimize_SAME_AS_I
 
 dispatch_opt = make_dispatcher_method(OptRewrite, 'optimize_',
-        default=OptRewrite.emit_operation)
+                                      default=OptRewrite.emit)
 optimize_guards = _findall(OptRewrite, 'optimize_', 'GUARD')
+dispatch_postprocess = make_dispatcher_method(OptRewrite, 'postprocess_')
diff --git a/rpython/jit/metainterp/optimizeopt/simplify.py 
b/rpython/jit/metainterp/optimizeopt/simplify.py
--- a/rpython/jit/metainterp/optimizeopt/simplify.py
+++ b/rpython/jit/metainterp/optimizeopt/simplify.py
@@ -8,16 +8,16 @@
         self.last_label_descr = None
         self.unroll = unroll
 
-    def emit_operation(self, op):
+    def emit(self, op):
         if op.is_guard():
             if self.optimizer.pendingfields is None:
                 self.optimizer.pendingfields = []
-        Optimization.emit_operation(self, op)
+        return Optimization.emit(self, op)
 
     def optimize_CALL_PURE_I(self, op):
         opnum = OpHelpers.call_for_descr(op.getdescr())
         newop = self.optimizer.replace_op_with(op, opnum)
-        self.emit_operation(newop)
+        return self.emit(newop)
     optimize_CALL_PURE_R = optimize_CALL_PURE_I
     optimize_CALL_PURE_F = optimize_CALL_PURE_I
     optimize_CALL_PURE_N = optimize_CALL_PURE_I
@@ -25,7 +25,7 @@
     def optimize_CALL_LOOPINVARIANT_I(self, op):
         opnum = OpHelpers.call_for_descr(op.getdescr())
         op = op.copy_and_change(opnum)
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_CALL_LOOPINVARIANT_R = optimize_CALL_LOOPINVARIANT_I
     optimize_CALL_LOOPINVARIANT_F = optimize_CALL_LOOPINVARIANT_I
     optimize_CALL_LOOPINVARIANT_N = optimize_CALL_LOOPINVARIANT_I
@@ -35,7 +35,7 @@
 
     def optimize_VIRTUAL_REF(self, op):
         newop = self.replace_op_with(op, rop.SAME_AS_R, [op.getarg(0)])
-        self.emit_operation(newop)
+        return self.emit(newop)
 
     def optimize_QUASIIMMUT_FIELD(self, op):
         # xxx ideally we could also kill the following GUARD_NOT_INVALIDATED
@@ -51,7 +51,7 @@
     #         if isinstance(descr, JitCellToken):
     #             return self.optimize_JUMP(op.copy_and_change(rop.JUMP))
     #         self.last_label_descr = op.getdescr()
-    #     self.emit_operation(op)
+    #     return self.emit(op)
 
     # def optimize_JUMP(self, op):
     #     if not self.unroll:
@@ -67,11 +67,11 @@
     #         else:
     #             assert len(descr.target_tokens) == 1
     #             op.setdescr(descr.target_tokens[0])
-    #     self.emit_operation(op)
+    #     return self.emit(op)
 
     def optimize_GUARD_FUTURE_CONDITION(self, op):
         self.optimizer.notice_guard_future_condition(op)
 
 dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
-        default=OptSimplify.emit_operation)
+                                      default=OptSimplify.emit)
 OptSimplify.propagate_forward = dispatch_opt
diff --git a/rpython/jit/metainterp/optimizeopt/virtualize.py 
b/rpython/jit/metainterp/optimizeopt/virtualize.py
--- a/rpython/jit/metainterp/optimizeopt/virtualize.py
+++ b/rpython/jit/metainterp/optimizeopt/virtualize.py
@@ -13,6 +13,7 @@
     "Virtualize objects until they escape."
 
     _last_guard_not_forced_2 = None
+    _finish_guard_op = None
 
     def make_virtual(self, known_class, source_op, descr):
         opinfo = info.InstancePtrInfo(descr, known_class, is_virtual=True)
@@ -62,26 +63,27 @@
     def optimize_GUARD_NO_EXCEPTION(self, op):
         if self.last_emitted_operation is REMOVED:
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GUARD_NOT_FORCED(self, op):
         if self.last_emitted_operation is REMOVED:
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GUARD_NOT_FORCED_2(self, op):
         self._last_guard_not_forced_2 = op
 
     def optimize_FINISH(self, op):
-        if self._last_guard_not_forced_2 is not None:
-            guard_op = self._last_guard_not_forced_2
-            self.emit_operation(op)
+        self._finish_guard_op = self._last_guard_not_forced_2
+        return self.emit(op)
+
+    def postprocess_FINISH(self, op):
+        guard_op = self._finish_guard_op
+        if guard_op is not None:
             guard_op = self.optimizer.store_final_boxes_in_guard(guard_op, [])
             i = len(self.optimizer._newoperations) - 1
             assert i >= 0
             self.optimizer._newoperations.insert(i, guard_op)
-        else:
-            self.emit_operation(op)
 
     def optimize_CALL_MAY_FORCE_I(self, op):
         effectinfo = op.getdescr().get_extra_info()
@@ -89,7 +91,7 @@
         if oopspecindex == EffectInfo.OS_JIT_FORCE_VIRTUAL:
             if self._optimize_JIT_FORCE_VIRTUAL(op):
                 return
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_CALL_MAY_FORCE_R = optimize_CALL_MAY_FORCE_I
     optimize_CALL_MAY_FORCE_F = optimize_CALL_MAY_FORCE_I
     optimize_CALL_MAY_FORCE_N = optimize_CALL_MAY_FORCE_I
@@ -101,7 +103,7 @@
             opinfo = self.getptrinfo(op.getarg(2))
             if opinfo and opinfo.is_virtual():
                 return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_VIRTUAL_REF(self, op):
         # get some constants
@@ -119,10 +121,10 @@
         op.set_forwarded(newop)
         newop.set_forwarded(vrefvalue)
         token = ResOperation(rop.FORCE_TOKEN, [])
-        self.emit_operation(token)
         vrefvalue.setfield(descr_virtual_token, newop, token)
         vrefvalue.setfield(descr_forced, newop,
                            self.optimizer.cpu.ts.CONST_NULLREF)
+        return self.emit(token)
 
     def optimize_VIRTUAL_REF_FINISH(self, op):
         # This operation is used in two cases.  In normal cases, it
@@ -185,7 +187,7 @@
             self.make_equal_to(op, fieldop)
         else:
             self.make_nonnull(op.getarg(0))
-            self.emit_operation(op)
+            return self.emit(op)
     optimize_GETFIELD_GC_R = optimize_GETFIELD_GC_I
     optimize_GETFIELD_GC_F = optimize_GETFIELD_GC_I
 
@@ -197,7 +199,7 @@
                             self.get_box_replacement(op.getarg(1)))
         else:
             self.make_nonnull(struct)
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_NEW_WITH_VTABLE(self, op):
         known_class = ConstInt(op.getdescr().get_vtable())
@@ -211,36 +213,35 @@
         if sizebox is not None:
             self.make_varray(op.getdescr(), sizebox.getint(), op)
         else:
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_NEW_ARRAY_CLEAR(self, op):
         sizebox = self.get_constant_box(op.getarg(0))
         if sizebox is not None:
             self.make_varray(op.getdescr(), sizebox.getint(), op, clear=True)
         else:
-            self.emit_operation(op)        
+            return self.emit(op)
 
     def optimize_CALL_N(self, op):
         effectinfo = op.getdescr().get_extra_info()
         if effectinfo.oopspecindex == EffectInfo.OS_RAW_MALLOC_VARSIZE_CHAR:
-            self.do_RAW_MALLOC_VARSIZE_CHAR(op)
+            return self.do_RAW_MALLOC_VARSIZE_CHAR(op)
         elif effectinfo.oopspecindex == EffectInfo.OS_RAW_FREE:
-            self.do_RAW_FREE(op)
+            return self.do_RAW_FREE(op)
         elif effectinfo.oopspecindex == EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE:
             # we might end up having CALL here instead of COND_CALL
             info = self.getptrinfo(op.getarg(1))
             if info and info.is_virtual():
                 return
         else:
-            self.emit_operation(op)
+            return self.emit(op)
     optimize_CALL_R = optimize_CALL_N
     optimize_CALL_I = optimize_CALL_N
 
     def do_RAW_MALLOC_VARSIZE_CHAR(self, op):
         sizebox = self.get_constant_box(op.getarg(1))
         if sizebox is None:
-            self.emit_operation(op)
-            return
+            return self.emit(op)
         self.make_virtual_raw_memory(sizebox.getint(), op)
         self.last_emitted_operation = REMOVED
 
@@ -248,7 +249,7 @@
         opinfo = self.getrawptrinfo(op.getarg(1))
         if opinfo and opinfo.is_virtual():
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_INT_ADD(self, op):
         opinfo = self.getrawptrinfo(op.getarg(0), create=False)
@@ -261,7 +262,7 @@
                 isinstance(opinfo, info.RawSlicePtrInfo)):
                 self.make_virtual_raw_slice(offset, opinfo, op)
                 return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_ARRAYLEN_GC(self, op):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -269,7 +270,7 @@
             self.make_constant_int(op, opinfo.getlength())
         else:
             self.make_nonnull(op.getarg(0))
-            self.emit_operation(op)
+            return self.emit(op)
 
     def optimize_GETARRAYITEM_GC_I(self, op):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -283,7 +284,7 @@
                 self.make_equal_to(op, item)
                 return
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_GETARRAYITEM_GC_R = optimize_GETARRAYITEM_GC_I
     optimize_GETARRAYITEM_GC_F = optimize_GETARRAYITEM_GC_I
 
@@ -303,7 +304,7 @@
                                self.get_box_replacement(op.getarg(2)))
                 return
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
 
     def _unpack_arrayitem_raw_op(self, op, indexbox):
         index = indexbox.getint()
@@ -328,7 +329,7 @@
                     self.make_equal_to(op, itemvalue)
                     return
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_GETARRAYITEM_RAW_F = optimize_GETARRAYITEM_RAW_I
 
     def optimize_SETARRAYITEM_RAW(self, op):
@@ -344,7 +345,7 @@
                 except InvalidRawOperation:
                     pass
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
 
     def _unpack_raw_load_store_op(self, op, offsetbox):
         offset = offsetbox.getint()
@@ -366,7 +367,7 @@
                 else:
                     self.make_equal_to(op, itemop)
                     return
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_RAW_LOAD_F = optimize_RAW_LOAD_I
 
     def optimize_RAW_STORE(self, op):
@@ -380,7 +381,7 @@
                     return
                 except InvalidRawOperation:
                     pass
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_GETINTERIORFIELD_GC_I(self, op):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -396,7 +397,7 @@
                 self.make_equal_to(op, fld)
                 return
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_GETINTERIORFIELD_GC_R = optimize_GETINTERIORFIELD_GC_I
     optimize_GETINTERIORFIELD_GC_F = optimize_GETINTERIORFIELD_GC_I
 
@@ -410,10 +411,12 @@
                                        self.get_box_replacement(op.getarg(2)))
                 return
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
 
 
 dispatch_opt = make_dispatcher_method(OptVirtualize, 'optimize_',
-        default=OptVirtualize.emit_operation)
+                                      default=OptVirtualize.emit)
 
 OptVirtualize.propagate_forward = dispatch_opt
+dispatch_postprocess = make_dispatcher_method(OptVirtualize, 'postprocess_')
+OptVirtualize.propagate_postprocess = dispatch_postprocess
diff --git a/rpython/jit/metainterp/optimizeopt/vstring.py 
b/rpython/jit/metainterp/optimizeopt/vstring.py
--- a/rpython/jit/metainterp/optimizeopt/vstring.py
+++ b/rpython/jit/metainterp/optimizeopt/vstring.py
@@ -97,7 +97,7 @@
         newop = ResOperation(self.mode.NEWSTR, [lengthbox])
         if not we_are_translated():
             newop.name = 'FORCE'
-        optforce.emit_operation(newop)
+        optforce.emit_extra(newop)
         newop = optforce.getlastop()
         newop.set_forwarded(self)
         op = optforce.get_box_replacement(op)
@@ -120,7 +120,7 @@
         lengthop = ResOperation(mode.STRLEN, [op])
         lengthop.set_forwarded(self.getlenbound(mode))
         self.lgtop = lengthop
-        string_optimizer.emit_operation(lengthop)
+        string_optimizer.emit_extra(lengthop)
         return lengthop
 
     def make_guards(self, op, short, optimizer):
@@ -204,7 +204,7 @@
                 op = ResOperation(mode.STRSETITEM, [targetbox,
                                                     offsetbox,
                                                     charbox])
-                string_optimizer.emit_operation(op)
+                string_optimizer.emit_extra(op)
             offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
         return offsetbox
 
@@ -356,7 +356,7 @@
                                                   mode)
             srcoffsetbox = _int_add(string_optimizer, srcoffsetbox, CONST_1)
             assert not isinstance(targetbox, Const)# ConstPtr never makes sense
-            string_optimizer.emit_operation(ResOperation(mode.STRSETITEM,
+            string_optimizer.emit_extra(ResOperation(mode.STRSETITEM,
                     [targetbox, offsetbox, charbox]))
             offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
     else:
@@ -368,7 +368,7 @@
         op = ResOperation(mode.COPYSTRCONTENT, [srcbox, targetbox,
                                                 srcoffsetbox, offsetbox,
                                                 lengthbox])
-        string_optimizer.emit_operation(op)
+        string_optimizer.emit_extra(op)
         offsetbox = nextoffsetbox
     return offsetbox
 
@@ -412,7 +412,7 @@
     else:
         resbox = string_optimizer.replace_op_with(resbox, mode.STRGETITEM,
                                                   [strbox, indexbox])
-    string_optimizer.emit_operation(resbox)
+    string_optimizer.emit_extra(resbox)
     return resbox
 
 
@@ -422,6 +422,12 @@
     def setup(self):
         self.optimizer.optstring = self
 
+    def propagate_forward(self, op):
+        return dispatch_opt(self, op)
+
+    def propagate_postprocess(self, op):
+        return dispatch_postprocess(self, op)
+
     def make_vstring_plain(self, op, mode, length):
         vvalue = VStringPlainInfo(mode, True, length)
         op = self.replace_op_with(op, op.getopnum())
@@ -441,9 +447,9 @@
         return vvalue
 
     def optimize_NEWSTR(self, op):
-        self._optimize_NEWSTR(op, mode_string)
+        return self._optimize_NEWSTR(op, mode_string)
     def optimize_NEWUNICODE(self, op):
-        self._optimize_NEWSTR(op, mode_unicode)
+        return self._optimize_NEWSTR(op, mode_unicode)
 
     def _optimize_NEWSTR(self, op, mode):
         length_box = self.get_constant_box(op.getarg(0))
@@ -452,8 +458,13 @@
             self.make_vstring_plain(op, mode, length_box.getint())
         else:
             self.make_nonnull_str(op, mode)
-            self.emit_operation(op)
-            self.pure_from_args(mode.STRLEN, [op], op.getarg(0))
+            return self.emit(op)
+
+    def postprocess_NEWSTR(self, op):
+        self.pure_from_args(mode_string.STRLEN, [op], op.getarg(0))
+
+    def postprocess_NEWUNICODE(self, op):
+        self.pure_from_args(mode_unicode.STRLEN, [op], op.getarg(0))
 
     def optimize_STRSETITEM(self, op):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -464,17 +475,17 @@
             indexbox = self.get_constant_box(op.getarg(1))
             if indexbox is not None:
                 opinfo.strsetitem(indexbox.getint(),
-                              self.get_box_replacement(op.getarg(2)))
+                                  self.get_box_replacement(op.getarg(2)))
                 return
         self.make_nonnull(op.getarg(0))
-        self.emit_operation(op)
+        return self.emit(op)
 
     optimize_UNICODESETITEM = optimize_STRSETITEM
 
     def optimize_STRGETITEM(self, op):
-        self._optimize_STRGETITEM(op, mode_string)
+        return self._optimize_STRGETITEM(op, mode_string)
     def optimize_UNICODEGETITEM(self, op):
-        self._optimize_STRGETITEM(op, mode_unicode)
+        return self._optimize_STRGETITEM(op, mode_unicode)
 
     def _optimize_STRGETITEM(self, op, mode):
         self.strgetitem(op, op.getarg(0), op.getarg(1), mode)
@@ -514,9 +525,9 @@
         return _strgetitem(self, s, index, mode, op)
 
     def optimize_STRLEN(self, op):
-        self._optimize_STRLEN(op, mode_string)
+        return self._optimize_STRLEN(op, mode_string)
     def optimize_UNICODELEN(self, op):
-        self._optimize_STRLEN(op, mode_unicode)
+        return self._optimize_STRLEN(op, mode_unicode)
 
     def _optimize_STRLEN(self, op, mode):
         opinfo = self.getptrinfo(op.getarg(0))
@@ -525,13 +536,13 @@
             if lgtop is not None:
                 self.make_equal_to(op, lgtop)
                 return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def optimize_COPYSTRCONTENT(self, op):
-        self._optimize_COPYSTRCONTENT(op, mode_string)
+        return self._optimize_COPYSTRCONTENT(op, mode_string)
 
     def optimize_COPYUNICODECONTENT(self, op):
-        self._optimize_COPYSTRCONTENT(op, mode_unicode)
+        return self._optimize_COPYSTRCONTENT(op, mode_unicode)
 
     def _optimize_COPYSTRCONTENT(self, op, mode):
         # args: src dst srcstart dststart length
@@ -566,7 +577,7 @@
                         op.getarg(1), ConstInt(index + dst_start),
                         vresult,
                     ])
-                    self.emit_operation(new_op)
+                    self.emit_extra(new_op)
         else:
             copy_str_content(self, op.getarg(0), op.getarg(1), op.getarg(2),
                              op.getarg(3), op.getarg(4), mode,
@@ -581,13 +592,15 @@
         if oopspecindex != EffectInfo.OS_NONE:
             for value, meth in opt_call_oopspec_ops:
                 if oopspecindex == value:      # a match with the OS_STR_xxx
-                    if meth(self, op, mode_string):
-                        return
+                    handled, newop = meth(self, op, mode_string)
+                    if handled:
+                        return newop
                     break
                 if oopspecindex == value + EffectInfo._OS_offset_uni:
                     # a match with the OS_UNI_xxx
-                    if meth(self, op, mode_unicode):
-                        return
+                    handled, newop = meth(self, op, mode_unicode)
+                    if handled:
+                        return newop
                     break
             if oopspecindex == EffectInfo.OS_STR2UNICODE:
                 if self.opt_call_str_STR2UNICODE(op):
@@ -595,7 +608,7 @@
             if oopspecindex == EffectInfo.OS_SHRINK_ARRAY:
                 if self.opt_call_SHRINK_ARRAY(op):
                     return
-        self.emit_operation(op)
+        return self.emit(op)
     optimize_CALL_R = optimize_CALL_I
     optimize_CALL_F = optimize_CALL_I
     optimize_CALL_N = optimize_CALL_I
@@ -607,7 +620,7 @@
     def optimize_GUARD_NO_EXCEPTION(self, op):
         if self.last_emitted_operation is REMOVED:
             return
-        self.emit_operation(op)
+        return self.emit(op)
 
     def opt_call_str_STR2UNICODE(self, op):
         # Constant-fold unicode("constant string").
@@ -635,7 +648,7 @@
                                  self.get_box_replacement(op.getarg(1)),
                                  self.get_box_replacement(op.getarg(2)))
         self.last_emitted_operation = REMOVED
-        return True
+        return True, None
 
     def opt_call_stroruni_STR_SLICE(self, op, mode):
         self.make_nonnull_str(op.getarg(1), mode)
@@ -651,7 +664,7 @@
         #    value = self.make_vstring_plain(op, mode, -1)
         #    value.setup_slice(vstr._chars, vstart.getint(),
         #                      vstop.getint())
-        #    return True
+        #    return True, None
         #
         startbox = op.getarg(2)
         strbox = op.getarg(1)
@@ -664,7 +677,7 @@
         #
         self.make_vstring_slice(op, strbox, startbox, mode, lengthbox)
         self.last_emitted_operation = REMOVED
-        return True
+        return True, None
 
     @specialize.arg(2)
     def opt_call_stroruni_STR_EQUAL(self, op, mode):
@@ -687,25 +700,28 @@
             l1box.value != l2box.value):
             # statically known to have a different length
             self.make_constant(op, CONST_0)
-            return True
+            return True, None
         #
-        if self.handle_str_equal_level1(arg1, arg2, op, mode):
-            return True
-        if self.handle_str_equal_level1(arg2, arg1, op, mode):
-            return True
-        if self.handle_str_equal_level2(arg1, arg2, op, mode):
-            return True
-        if self.handle_str_equal_level2(arg2, arg1, op, mode):
-            return True
+        handled, result = self.handle_str_equal_level1(arg1, arg2, op, mode)
+        if handled:
+            return True, result
+        handled, result = self.handle_str_equal_level1(arg2, arg1, op, mode)
+        if handled:
+            return True, result
+        handled, result = self.handle_str_equal_level2(arg1, arg2, op, mode)
+        if handled:
+            return True, result
+        handled, result = self.handle_str_equal_level2(arg2, arg1, op, mode)
+        if handled:
+            return True, result
         #
         if i1 and i1.is_nonnull() and i2 and i2.is_nonnull():
             if l1box is not None and l2box is not None and 
l1box.same_box(l2box):
                 do = EffectInfo.OS_STREQ_LENGTHOK
             else:
                 do = EffectInfo.OS_STREQ_NONNULL
-            self.generate_modified_call(do, [arg1, arg2], op, mode)
-            return True
-        return False
+            return True, self.generate_modified_call(do, [arg1, arg2], op, 
mode)
+        return False, None
 
     def handle_str_equal_level1(self, arg1, arg2, resultop, mode):
         i1 = self.getptrinfo(arg1)
@@ -728,7 +744,7 @@
                                               [lengthbox, CONST_0],
                                               descr=DONT_CHANGE)
                     seo(op)
-                    return True
+                    return True, None
             if l2box.value == 1:
                 if i1:
                     l1box = i1.getstrlen(arg1, self, mode, False)
@@ -742,30 +758,28 @@
                     op = self.optimizer.replace_op_with(resultop, rop.INT_EQ,
                                 [vchar1, vchar2], descr=DONT_CHANGE)
                     seo(op)
-                    return True
+                    return True, None
                 if isinstance(i1, VStringSliceInfo):
                     vchar = self.strgetitem(None, arg2, optimizer.CONST_0,
                                             mode)
                     do = EffectInfo.OS_STREQ_SLICE_CHAR
-                    self.generate_modified_call(do, [i1.s, i1.start,
-                                                     i1.lgtop, vchar],
-                                                     resultop, mode)
-                    return True
+                    return True, self.generate_modified_call(do, [i1.s, 
i1.start,
+                                                                  i1.lgtop, 
vchar],
+                                                             resultop, mode)
         #
         if i2 and i2.is_null():
             if i1 and i1.is_nonnull():
                 self.make_constant(resultop, CONST_0)
-                return True
+                return True, None
             if i1 and i1.is_null():
                 self.make_constant(resultop, CONST_1)
-                return True
+                return True, None
             op = self.optimizer.replace_op_with(resultop, rop.PTR_EQ,
                                                 [arg1, llhelper.CONST_NULL],
                                                 descr=DONT_CHANGE)
-            self.emit_operation(op)
-            return True
+            return True, self.emit(op)
         #
-        return False
+        return False, None
 
     def handle_str_equal_level2(self, arg1, arg2, resultbox, mode):
         i1 = self.getptrinfo(arg1)
@@ -782,25 +796,23 @@
                         do = EffectInfo.OS_STREQ_NONNULL_CHAR
                     else:
                         do = EffectInfo.OS_STREQ_CHECKNULL_CHAR
-                    self.generate_modified_call(do, [arg1, vchar],
-                                                resultbox, mode)
-                    return True
+                    return True, self.generate_modified_call(do, [arg1, vchar],
+                                                             resultbox, mode)
             #
         if isinstance(i1, VStringSliceInfo) and i1.is_virtual():
             if i2 and i2.is_nonnull():
                 do = EffectInfo.OS_STREQ_SLICE_NONNULL
             else:
                 do = EffectInfo.OS_STREQ_SLICE_CHECKNULL
-            self.generate_modified_call(do, [i1.s, i1.start, i1.lgtop,
-                                             arg2], resultbox, mode)
-            return True
-        return False
+            return True, self.generate_modified_call(do, [i1.s, i1.start, 
i1.lgtop,
+                                                          arg2], resultbox, 
mode)
+        return False, None
 
     def opt_call_stroruni_STR_CMP(self, op, mode):
         i1 = self.getptrinfo(op.getarg(1))
         i2 = self.getptrinfo(op.getarg(2))
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to