[pypy-commit] pypy fix-sre-problems: fix issue 2777:

cfbolz pypy.commits at gmail.com
Mon Mar 26 12:01:48 EDT 2018


Author: Carl Friedrich Bolz-Tereick <cfbolz at gmx.de>
Branch: fix-sre-problems
Changeset: r94135:b3264f1f3592
Date: 2018-03-26 18:01 +0200
http://bitbucket.org/pypy/pypy/changeset/b3264f1f3592/

Log:	fix issue 2777:

	before this commit, the following problem could occur: when the tags
	of the opencoder overflow, it raises SwitchToBlackHole in the middle
	of a jitcode opcode. Potentially, in the middle of a guard, *before*
	the guard changed the pc on the MIFrame! That way, the blackhole
	interpreter would continue in the wrong branch :-(.

	Fix this by changing the interface slightly: opencoder.Trace.record
	will now just not record anything, if the tags overflow. Instead, it
	will set a flag on itself, which the metatracer needs to check after
	every opcode, to find out whether to stop tracing.

diff --git a/rpython/jit/metainterp/history.py b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -701,6 +701,9 @@
     def length(self):
         return self.trace._count - len(self.trace.inputargs)
 
+    def trace_tag_overflow(self):
+        return self.trace.tag_overflow
+
     def get_trace_position(self):
         return self.trace.cut_point()
 
diff --git a/rpython/jit/metainterp/opencoder.py b/rpython/jit/metainterp/opencoder.py
--- a/rpython/jit/metainterp/opencoder.py
+++ b/rpython/jit/metainterp/opencoder.py
@@ -293,6 +293,7 @@
         self._start = len(inputargs)
         self._pos = self._start
         self.inputargs = inputargs
+        self.tag_overflow = False
 
     def append(self, v):
         model = get_model(self)
@@ -300,7 +301,8 @@
             # grow by 2X
             self._ops = self._ops + [rffi.cast(model.STORAGE_TP, 0)] * len(self._ops)
         if not model.MIN_VALUE <= v <= model.MAX_VALUE:
-            raise frontend_tag_overflow()
+            v = 0 # broken value, but that's fine, tracing will stop soon
+            self.tag_overflow = True
         self._ops[self._pos] = rffi.cast(model.STORAGE_TP, v)
         self._pos += 1
 
@@ -379,6 +381,7 @@
 
     def record_op(self, opnum, argboxes, descr=None):
         pos = self._index
+        old_pos = self._pos
         self.append(opnum)
         expected_arity = oparity[opnum]
         if expected_arity == -1:
@@ -397,6 +400,10 @@
         self._count += 1
         if opclasses[opnum].type != 'v':
             self._index += 1
+        if self.tag_overflow:
+            # potentially a broken op is left behind
+            # clean it up
+            self._pos = old_pos
         return pos
 
     def _encode_descr(self, descr):
@@ -424,10 +431,11 @@
         vref_array = self._list_of_boxes(vref_boxes)
         s = TopSnapshot(combine_uint(jitcode.index, pc), array, vable_array,
                         vref_array)
-        assert rffi.cast(lltype.Signed, self._ops[self._pos - 1]) == 0
         # guards have no descr
         self._snapshots.append(s)
-        self._ops[self._pos - 1] = rffi.cast(get_model(self).STORAGE_TP, len(self._snapshots) - 1)
+        if not self.tag_overflow: # otherwise we're broken anyway
+            assert rffi.cast(lltype.Signed, self._ops[self._pos - 1]) == 0
+            self._ops[self._pos - 1] = rffi.cast(get_model(self).STORAGE_TP, len(self._snapshots) - 1)
         return s
 
     def create_empty_top_snapshot(self, vable_boxes, vref_boxes):
@@ -436,10 +444,11 @@
         vref_array = self._list_of_boxes(vref_boxes)
         s = TopSnapshot(combine_uint(2**16 - 1, 0), [], vable_array,
                         vref_array)
-        assert rffi.cast(lltype.Signed, self._ops[self._pos - 1]) == 0
         # guards have no descr
         self._snapshots.append(s)
-        self._ops[self._pos - 1] = rffi.cast(get_model(self).STORAGE_TP, len(self._snapshots) - 1)
+        if not self.tag_overflow: # otherwise we're broken anyway
+            assert rffi.cast(lltype.Signed, self._ops[self._pos - 1]) == 0
+            self._ops[self._pos - 1] = rffi.cast(get_model(self).STORAGE_TP, len(self._snapshots) - 1)
         return s
 
     def create_snapshot(self, jitcode, pc, frame, flag):
diff --git a/rpython/jit/metainterp/pyjitpl.py b/rpython/jit/metainterp/pyjitpl.py
--- a/rpython/jit/metainterp/pyjitpl.py
+++ b/rpython/jit/metainterp/pyjitpl.py
@@ -2384,7 +2384,8 @@
 
     def blackhole_if_trace_too_long(self):
         warmrunnerstate = self.jitdriver_sd.warmstate
-        if self.history.length() > warmrunnerstate.trace_limit:
+        if (self.history.length() > warmrunnerstate.trace_limit or
+                self.history.trace_tag_overflow()):
             jd_sd, greenkey_of_huge_function = self.find_biggest_function()
             self.history.trace.done()
             self.staticdata.stats.record_aborted(greenkey_of_huge_function)
diff --git a/rpython/jit/metainterp/test/test_ajit.py b/rpython/jit/metainterp/test/test_ajit.py
--- a/rpython/jit/metainterp/test/test_ajit.py
+++ b/rpython/jit/metainterp/test/test_ajit.py
@@ -4661,3 +4661,36 @@
 
         f() # finishes
         self.meta_interp(f, [])
+
+    def test_trace_too_long_bug(self):
+        driver = JitDriver(greens=[], reds=['i'])
+        @unroll_safe
+        def match(s):
+            l = len(s)
+            p = 0
+            for i in range(2500): # produces too long trace
+                c = s[p]
+                if c != 'a':
+                    return False
+                p += 1
+                if p >= l:
+                    return True
+                c = s[p]
+                if c != '\n':
+                    p += 1
+                    if p >= l:
+                        return True
+                else:
+                    return False
+            return True
+
+        def f(i):
+            while i > 0:
+                driver.jit_merge_point(i=i)
+                match('a' * (500 * i))
+                i -= 1
+            return i
+
+        res = self.meta_interp(f, [10])
+        assert res == f(10)
+
diff --git a/rpython/jit/metainterp/test/test_opencoder.py b/rpython/jit/metainterp/test/test_opencoder.py
--- a/rpython/jit/metainterp/test/test_opencoder.py
+++ b/rpython/jit/metainterp/test/test_opencoder.py
@@ -209,5 +209,8 @@
     def test_tag_overflow(self):
         t = Trace([], metainterp_sd)
         i0 = FakeOp(100000)
-        py.test.raises(SwitchToBlackhole, t.record_op, rop.FINISH, [i0])
-        assert t.unpack() == ([], [])
+        # if we overflow, we can keep recording
+        for i in range(10):
+            t.record_op(rop.FINISH, [i0])
+            assert t.unpack() == ([], [])
+        assert t.tag_overflow
diff --git a/rpython/rlib/rsre/test/test_zjit.py b/rpython/rlib/rsre/test/test_zjit.py
--- a/rpython/rlib/rsre/test/test_zjit.py
+++ b/rpython/rlib/rsre/test/test_zjit.py
@@ -11,6 +11,8 @@
     match = None
     for i in range(repeat):
         match = rsre_core.match(r, string)
+        if match is None:
+            return -1
     if match is None:
         return -1
     else:
@@ -166,3 +168,9 @@
         res = self.meta_interp_search(r"b+", "a"*30 + "b")
         assert res == 30
         self.check_resops(call=0)
+
+    def test_match_jit_bug(self):
+        pattern = ".a" * 2500
+        text = "a" * 6000
+        res = self.meta_interp_match(pattern, text, repeat=10)
+        assert res != -1


More information about the pypy-commit mailing list