[pypy-svn] r70714 - in pypy/trunk/pypy/jit/metainterp: . test

cfbolz at codespeak.net cfbolz at codespeak.net
Tue Jan 19 18:01:17 CET 2010


Author: cfbolz
Date: Tue Jan 19 18:01:16 2010
New Revision: 70714

Modified:
   pypy/trunk/pypy/jit/metainterp/compile.py
   pypy/trunk/pypy/jit/metainterp/history.py
   pypy/trunk/pypy/jit/metainterp/optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/pyjitpl.py
   pypy/trunk/pypy/jit/metainterp/resoperation.py
   pypy/trunk/pypy/jit/metainterp/test/test_basic.py
   pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
   pypy/trunk/pypy/jit/metainterp/test/test_virtualref.py
Log:
Fix the bug that the previous test showed: optimizeopt can mutate operations in
ways that become a problem when the loop later proves to be invalid. This fix is
conservative in the sense that it always makes a copy of all operations, instead
of trying to make optimizeopt more careful. I wasn't sure enough that I would
catch all cases I I had tried that.


Modified: pypy/trunk/pypy/jit/metainterp/compile.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/compile.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/compile.py	Tue Jan 19 18:01:16 2010
@@ -54,9 +54,11 @@
     for box in loop.inputargs:
         assert isinstance(box, Box)
     if start > 0:
-        loop.operations = history.operations[start:]
+        ops = history.operations[start:]
     else:
-        loop.operations = history.operations
+        ops = history.operations
+    # make a copy, because optimize_loop can mutate the ops and descrs
+    loop.operations = [op.clone() for op in ops]
     metainterp_sd = metainterp.staticdata
     loop_token = make_loop_token(len(loop.inputargs))
     loop.token = loop_token
@@ -203,11 +205,18 @@
 class ResumeDescr(AbstractFailDescr):
     def __init__(self, original_greenkey):
         self.original_greenkey = original_greenkey
+    def _clone_if_mutable(self):
+        raise NotImplementedError
 
 class ResumeGuardDescr(ResumeDescr):
     counter = 0
-    # this class also gets attributes stored by resume.py code
+    # this class also gets the following attributes stored by resume.py code
+    rd_snapshot = None
+    rd_frame_info_list = None
     rd_numb = None
+    rd_consts = None
+    rd_virtuals = None
+    rd_pendingfields = None
 
     def __init__(self, metainterp_sd, original_greenkey):
         ResumeDescr.__init__(self, original_greenkey)
@@ -232,6 +241,17 @@
                                new_loop.operations)
 
 
+    def _clone_if_mutable(self):
+        res = self.__class__(self.metainterp_sd, self.original_greenkey)
+        # XXX a bit ugly to have to list them all here
+        res.rd_snapshot = self.rd_snapshot
+        res.rd_frame_info_list = self.rd_frame_info_list
+        res.rd_numb = self.rd_numb
+        res.rd_consts = self.rd_consts
+        res.rd_virtuals = self.rd_virtuals
+        res.rd_pendingfields = self.rd_pendingfields
+        return res
+
 class ResumeGuardForcedDescr(ResumeGuardDescr):
 
     def handle_fail(self, metainterp_sd):
@@ -337,13 +357,15 @@
     # it does not work -- i.e. none of the existing old_loop_tokens match.
     new_loop = create_empty_loop(metainterp)
     new_loop.inputargs = metainterp.history.inputargs
-    new_loop.operations = metainterp.history.operations
+    # clone ops, as optimize_bridge can mutate the ops
+    new_loop.operations = [op.clone() for op in metainterp.history.operations]
     metainterp_sd = metainterp.staticdata
     try:
         target_loop_token = metainterp_sd.state.optimize_bridge(metainterp_sd,
                                                                 old_loop_tokens,
                                                                 new_loop)
     except InvalidLoop:
+        assert 0
         return None
     # Did it work?
     if target_loop_token is not None:

Modified: pypy/trunk/pypy/jit/metainterp/history.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/history.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/history.py	Tue Jan 19 18:01:16 2010
@@ -122,6 +122,9 @@
     def repr_of_descr(self):
         return '%r' % (self,)
 
+    def _clone_if_mutable(self):
+        return self
+
 class AbstractFailDescr(AbstractDescr):
     index = -1
 

Modified: pypy/trunk/pypy/jit/metainterp/optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/optimizeopt.py	Tue Jan 19 18:01:16 2010
@@ -515,20 +515,16 @@
         # accumulate counters
         self.resumedata_memo.update_counters(self.metainterp_sd.profiler)
 
-    def emit_operation(self, op, must_clone=True):
+    def emit_operation(self, op):
         self.heap_op_optimizer.emitting_operation(op)
-        self._emit_operation(op, must_clone)
+        self._emit_operation(op)
 
-    def _emit_operation(self, op, must_clone=True):
+    def _emit_operation(self, op):
         for i in range(len(op.args)):
             arg = op.args[i]
             if arg in self.values:
                 box = self.values[arg].force_box()
-                if box is not arg:
-                    if must_clone:
-                        op = op.clone()
-                        must_clone = False
-                    op.args[i] = box
+                op.args[i] = box
         self.metainterp_sd.profiler.count(jitprof.OPT_OPS)
         if op.is_guard():
             self.metainterp_sd.profiler.count(jitprof.OPT_GUARDS)
@@ -588,9 +584,8 @@
         for i in range(len(specnodes)):
             value = self.getvalue(op.args[i])
             specnodes[i].teardown_virtual_node(self, value, exitargs)
-        op2 = op.clone()
-        op2.args = exitargs[:]
-        self.emit_operation(op2, must_clone=False)
+        op.args = exitargs[:]
+        self.emit_operation(op)
 
     def optimize_guard(self, op, constbox, emit_operation=True):
         value = self.getvalue(op.args[0])

Modified: pypy/trunk/pypy/jit/metainterp/pyjitpl.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/pyjitpl.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/pyjitpl.py	Tue Jan 19 18:01:16 2010
@@ -1613,17 +1613,9 @@
                     # we cannot reconstruct the beginning of the proper loop
                     raise GiveUp
 
-                oldops = self.history.operations[:]
                 # raises in case it works -- which is the common case
                 self.compile(original_boxes, live_arg_boxes, start)
-                # creation of the loop was cancelled!  Patch
-                # history.operations so that it contains again
-                # exactly its old list of operations...
-                # xxx maybe we could patch history.operations with
-                # Nones after calling self.compile() instead of
-                # before...  xxx maybe we should just raise GiveUp
-                del self.history.operations[:]
-                self.history.operations.extend(oldops)
+                # creation of the loop was cancelled!
 
         # Otherwise, no loop found so far, so continue tracing.
         start = len(self.history.operations)
@@ -1661,6 +1653,7 @@
                                               greenkey, start)
         if loop_token is not None: # raise if it *worked* correctly
             raise GenerateMergePoint(live_arg_boxes, loop_token)
+        self.history.operations.pop()     # remove the JUMP
 
     def compile_bridge(self, live_arg_boxes):
         num_green_args = self.staticdata.num_green_args

Modified: pypy/trunk/pypy/jit/metainterp/resoperation.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/resoperation.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/resoperation.py	Tue Jan 19 18:01:16 2010
@@ -31,7 +31,11 @@
         self.descr = descr
 
     def clone(self):
-        op = ResOperation(self.opnum, self.args, self.result, self.descr)
+        descr = self.descr
+        if descr is not None:
+            descr = descr._clone_if_mutable()
+        op = ResOperation(self.opnum, self.args, self.result, descr)
+        op.fail_args = self.fail_args
         if not we_are_translated():
             op.name = self.name
             op.pc = self.pc

Modified: pypy/trunk/pypy/jit/metainterp/test/test_basic.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_basic.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_basic.py	Tue Jan 19 18:01:16 2010
@@ -1249,7 +1249,6 @@
         assert res == 3 * 21
         self.check_loops(call=1)
 
-    @py.test.mark.xfail
     def test_bug_optimizeopt_mutates_ops(self):
         myjitdriver = JitDriver(greens = [], reds = ['x', 'res', 'a', 'const'])
         class A(object):
@@ -1284,7 +1283,7 @@
                     const = 1
             return res
         res = self.meta_interp(f, [21])
-        assert res == 120
+        assert res == f(21)
         
 
 class TestOOtype(BasicTests, OOJitMixin):

Modified: pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_optimizeopt.py	Tue Jan 19 18:01:16 2010
@@ -207,8 +207,9 @@
 
 class Storage(compile.ResumeGuardDescr):
     "for tests."
-    def __init__(self):
-        pass
+    def __init__(self, metainterp_sd=None, original_greenkey=None):
+        self.metainterp_sd = metainterp_sd
+        self.original_greenkey = original_greenkey
     def store_final_boxes(self, op, boxes):
         op.fail_args = boxes
     def __eq__(self, other):

Modified: pypy/trunk/pypy/jit/metainterp/test/test_virtualref.py
==============================================================================
--- pypy/trunk/pypy/jit/metainterp/test/test_virtualref.py	(original)
+++ pypy/trunk/pypy/jit/metainterp/test/test_virtualref.py	Tue Jan 19 18:01:16 2010
@@ -69,7 +69,8 @@
         assert res == 5
         self.check_operations_history(virtual_ref=1, guard_not_forced=1)
         #
-        [guard_op] = [op for op in self.metainterp.history.operations
+        ops = self.metainterp.staticdata.stats.loops[0].operations
+        [guard_op] = [op for op in ops
                          if op.opnum == rop.GUARD_NOT_FORCED]
         bxs1 = [box for box in guard_op.fail_args
                   if str(box._getrepr_()).endswith('.X')]



More information about the Pypy-commit mailing list