[pypy-commit] pypy default: Before constant-folding pure GC heap operations, check in detail that it

arigo noreply at buildbot.pypy.org
Mon Nov 9 03:35:41 EST 2015


Author: Armin Rigo <arigo at tunes.org>
Branch: 
Changeset: r80606:ebe258311483
Date: 2015-11-09 09:23 +0100
http://bitbucket.org/pypy/pypy/changeset/ebe258311483/

Log:	Before constant-folding pure GC heap operations, check in detail
	that it is valid (not a wrong-typed or out-of-bound access). We can
	only do it exactly if supports_guard_gc_type, but that's the only
	case in which we should get examples that fail the check (even if
	rarely): when optimizing the unrolled loop.

diff --git a/rpython/jit/backend/llgraph/runner.py b/rpython/jit/backend/llgraph/runner.py
--- a/rpython/jit/backend/llgraph/runner.py
+++ b/rpython/jit/backend/llgraph/runner.py
@@ -7,6 +7,7 @@
 from rpython.jit.metainterp.history import INT, REF, FLOAT, VOID
 from rpython.jit.metainterp.resoperation import rop
 from rpython.jit.metainterp.optimizeopt import intbounds
+from rpython.jit.metainterp.optimize import SpeculativeError
 from rpython.jit.codewriter import longlong, heaptracker
 from rpython.jit.codewriter.effectinfo import EffectInfo
 
@@ -197,6 +198,7 @@
         return intbounds.get_integer_max(
             not _is_signed_kind(self.FIELD), rffi.sizeof(self.FIELD))
 
+
 def _is_signed_kind(TYPE):
     return (TYPE is not lltype.Bool and isinstance(TYPE, lltype.Number) and
             rffi.cast(TYPE, -1) == -1)
@@ -949,6 +951,35 @@
     def store_fail_descr(self, deadframe, descr):
         pass # I *think*
 
+    def protect_speculative_field(self, p, fielddescr):
+        if not p:
+            raise SpeculativeError
+        p = p._obj.container._as_ptr()
+        try:
+            lltype.cast_pointer(lltype.Ptr(fielddescr.S), p)
+        except lltype.InvalidCast:
+            raise SpeculativeError
+
+    def protect_speculative_array(self, p, arraydescr):
+        if not p:
+            raise SpeculativeError
+        p = p._obj.container
+        if lltype.typeOf(p) != arraydescr.A:
+            raise SpeculativeError
+
+    def protect_speculative_string(self, p):
+        if not p:
+            raise SpeculativeError
+        p = p._obj.container
+        if lltype.typeOf(p) != rstr.STR:
+            raise SpeculativeError
+
+    def protect_speculative_unicode(self, p):
+        if not p:
+            raise SpeculativeError
+        p = p._obj.container
+        if lltype.typeOf(p) != rstr.UNICODE:
+            raise SpeculativeError
 
 
 class LLDeadFrame(object):
diff --git a/rpython/jit/backend/llsupport/descr.py b/rpython/jit/backend/llsupport/descr.py
--- a/rpython/jit/backend/llsupport/descr.py
+++ b/rpython/jit/backend/llsupport/descr.py
@@ -57,6 +57,16 @@
     def is_object(self):
         return bool(self.vtable)
 
+    def is_valid_class_for(self, struct):
+        objptr = lltype.cast_opaque_ptr(rclass.OBJECTPTR, struct)
+        cls = llmemory.cast_adr_to_ptr(
+            heaptracker.int2adr(self.get_vtable()),
+            lltype.Ptr(rclass.OBJECT_VTABLE))
+        # this first comparison is necessary, since we want to make sure
+        # that vtable for JitVirtualRef is the same without actually reading
+        # fields
+        return objptr.typeptr == cls or rclass.ll_isinstance(objptr, cls)
+
     def is_immutable(self):
         return self.immutable_flag
 
@@ -129,18 +139,11 @@
     def __repr__(self):
         return 'FieldDescr<%s>' % (self.name,)
 
-    def check_correct_type(self, struct):
+    def assert_correct_type(self, struct):
+        # similar to cpu.protect_speculative_field(), but works also
+        # if supports_guard_gc_type is false (and is allowed to crash).
         if self.parent_descr.is_object():
-            cls = llmemory.cast_adr_to_ptr(
-                heaptracker.int2adr(self.parent_descr.get_vtable()),
-                lltype.Ptr(rclass.OBJECT_VTABLE))
-            tpptr = lltype.cast_opaque_ptr(rclass.OBJECTPTR, struct).typeptr
-            # this comparison is necessary, since we want to make sure
-            # that vtable for JitVirtualRef is the same without actually reading
-            # fields
-            if tpptr != cls:
-                assert rclass.ll_isinstance(lltype.cast_opaque_ptr(
-                    rclass.OBJECTPTR, struct), cls)
+            assert self.parent_descr.is_valid_class_for(struct)
         else:
             pass
 
diff --git a/rpython/jit/backend/llsupport/gc.py b/rpython/jit/backend/llsupport/gc.py
--- a/rpython/jit/backend/llsupport/gc.py
+++ b/rpython/jit/backend/llsupport/gc.py
@@ -729,7 +729,8 @@
         return (infobits_offset, self._T_IS_RPYTHON_INSTANCE_BYTE)
 
     def get_actual_typeid(self, gcptr):
-        # Read the whole GC header word.  The typeid is the lower half-word.
+        # Read the whole GC header word.  Return the typeid from the
+        # lower half-word.
         hdr = rffi.cast(self.HDRPTR, gcptr)
         type_id = llop.extract_ushort(llgroup.HALFWORD, hdr.tid)
         return llop.combine_ushort(lltype.Signed, type_id, 0)
diff --git a/rpython/jit/backend/llsupport/llmodel.py b/rpython/jit/backend/llsupport/llmodel.py
--- a/rpython/jit/backend/llsupport/llmodel.py
+++ b/rpython/jit/backend/llsupport/llmodel.py
@@ -6,6 +6,7 @@
 from rpython.rtyper.llannotation import lltype_to_annotation
 from rpython.rlib.objectmodel import we_are_translated, specialize
 from rpython.jit.metainterp import history, compile
+from rpython.jit.metainterp.optimize import SpeculativeError
 from rpython.jit.codewriter import heaptracker, longlong
 from rpython.jit.backend.model import AbstractCPU
 from rpython.jit.backend.llsupport import symbolic, jitframe
@@ -529,6 +530,34 @@
         assert self.supports_guard_gc_type
         return self.gc_ll_descr.get_actual_typeid(gcptr)
 
+    def protect_speculative_field(self, gcptr, fielddescr):
+        if not gcptr:
+            raise SpeculativeError
+        if self.supports_guard_gc_type:
+            assert isinstance(fielddescr, FieldDescr)
+            sizedescr = fielddescr.parent_descr
+            if sizedescr.is_object():
+                if (not self.check_is_object(gcptr) or
+                    not sizedescr.is_valid_class_for(gcptr)):
+                    raise SpeculativeError
+            else:
+                if self.get_actual_typeid(gcptr) != sizedescr.tid:
+                    raise SpeculativeError
+
+    def protect_speculative_array(self, gcptr, arraydescr):
+        if not gcptr:
+            raise SpeculativeError
+        if self.supports_guard_gc_type:
+            assert isinstance(arraydescr, ArrayDescr)
+            if self.get_actual_typeid(gcptr) != arraydescr.tid:
+                raise SpeculativeError
+
+    def protect_speculative_string(self, gcptr):
+        self.protect_speculative_array(gcptr, self.gc_ll_descr.str_descr)
+
+    def protect_speculative_unicode(self, gcptr):
+        self.protect_speculative_array(gcptr, self.gc_ll_descr.unicode_descr)
+
     # ____________________________________________________________
 
     def bh_arraylen_gc(self, array, arraydescr):
@@ -633,21 +662,21 @@
     def bh_getfield_gc_i(self, struct, fielddescr):
         ofs, size, sign = self.unpack_fielddescr_size(fielddescr)
         if isinstance(lltype.typeOf(struct), lltype.Ptr):
-            fielddescr.check_correct_type(struct)
+            fielddescr.assert_correct_type(struct)
         return self.read_int_at_mem(struct, ofs, size, sign)
 
     @specialize.argtype(1)
     def bh_getfield_gc_r(self, struct, fielddescr):
         ofs = self.unpack_fielddescr(fielddescr)
         if isinstance(lltype.typeOf(struct), lltype.Ptr):
-            fielddescr.check_correct_type(struct)
+            fielddescr.assert_correct_type(struct)
         return self.read_ref_at_mem(struct, ofs)
 
     @specialize.argtype(1)
     def bh_getfield_gc_f(self, struct, fielddescr):
         ofs = self.unpack_fielddescr(fielddescr)
         if isinstance(lltype.typeOf(struct), lltype.Ptr):
-            fielddescr.check_correct_type(struct)
+            fielddescr.assert_correct_type(struct)
         return self.read_float_at_mem(struct, ofs)
 
     bh_getfield_raw_i = bh_getfield_gc_i
@@ -658,20 +687,20 @@
     def bh_setfield_gc_i(self, struct, newvalue, fielddescr):
         ofs, size, _ = self.unpack_fielddescr_size(fielddescr)
         if isinstance(lltype.typeOf(struct), lltype.Ptr):
-            fielddescr.check_correct_type(struct)
+            fielddescr.assert_correct_type(struct)
         self.write_int_at_mem(struct, ofs, size, newvalue)
 
     def bh_setfield_gc_r(self, struct, newvalue, fielddescr):
         ofs = self.unpack_fielddescr(fielddescr)
         if isinstance(lltype.typeOf(struct), lltype.Ptr):
-            fielddescr.check_correct_type(struct)
+            fielddescr.assert_correct_type(struct)
         self.write_ref_at_mem(struct, ofs, newvalue)
 
     @specialize.argtype(1)
     def bh_setfield_gc_f(self, struct, newvalue, fielddescr):
         ofs = self.unpack_fielddescr(fielddescr)
         if isinstance(lltype.typeOf(struct), lltype.Ptr):
-            fielddescr.check_correct_type(struct)
+            fielddescr.assert_correct_type(struct)
         self.write_float_at_mem(struct, ofs, newvalue)
 
     bh_setfield_raw_i = bh_setfield_gc_i
diff --git a/rpython/jit/backend/llsupport/test/test_zrpy_gc_direct.py b/rpython/jit/backend/llsupport/test/test_zrpy_gc_direct.py
--- a/rpython/jit/backend/llsupport/test/test_zrpy_gc_direct.py
+++ b/rpython/jit/backend/llsupport/test/test_zrpy_gc_direct.py
@@ -3,6 +3,7 @@
 from rpython.jit.metainterp.history import JitCellToken, NoStats
 from rpython.jit.metainterp.history import BasicFinalDescr, BasicFailDescr
 from rpython.jit.metainterp.gc import get_description
+from rpython.jit.metainterp.optimize import SpeculativeError
 from rpython.annotator.listdef import s_list_of_strings
 from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rtyper.rclass import getclassrepr, getinstancerepr
@@ -19,7 +20,7 @@
     class C(B):
         pass
     def main(argv):
-        A(); B(); C()
+        A(); B().foo = len(argv); C()
         return 0
 
     t = TranslationContext()
@@ -48,6 +49,14 @@
 
     descr_B = cpu.sizeof(LLB, ptr_vtable_B)
     typeid_B = descr_B.get_type_id()
+    fielddescr_B = cpu.fielddescrof(LLB, 'inst_foo')
+
+    LLD = lltype.GcStruct('D', ('dd', lltype.Signed))
+    descr_D = cpu.sizeof(LLD)
+    fielddescr_D = cpu.fielddescrof(LLD, 'dd')
+
+    ARRAY = lltype.GcArray(lltype.Signed)
+    arraydescr = cpu.arraydescrof(ARRAY)
 
     loop1 = parse("""
     [p0]
@@ -123,6 +132,38 @@
             if token is token3:    # guard_is_object
                 print int(cpu.check_is_object(p0))
 
+        for p0 in [lltype.nullptr(llmemory.GCREF.TO),
+                   rffi.cast(llmemory.GCREF, A()),
+                   rffi.cast(llmemory.GCREF, B()),
+                   rffi.cast(llmemory.GCREF, C()),
+                   rffi.cast(llmemory.GCREF, lltype.malloc(LLD)),
+                   rffi.cast(llmemory.GCREF, lltype.malloc(ARRAY, 5)),
+                   rffi.cast(llmemory.GCREF, "foobar"),
+                   rffi.cast(llmemory.GCREF, u"foobaz")]:
+            results = ['B', 'D', 'A', 'S', 'U']
+            try:
+                cpu.protect_speculative_field(p0, fielddescr_B)
+            except SpeculativeError:
+                results[0] = '-'
+            try:
+                cpu.protect_speculative_field(p0, fielddescr_D)
+            except SpeculativeError:
+                results[1] = '-'
+            try:
+                cpu.protect_speculative_array(p0, arraydescr)
+            except SpeculativeError:
+                results[2] = '-'
+            try:
+                cpu.protect_speculative_string(p0)
+            except SpeculativeError:
+                results[3] = '-'
+            try:
+                cpu.protect_speculative_unicode(p0)
+            except SpeculativeError:
+                results[4] = '-'
+            print ''.join(results)
+
+
     call_initial_function(t, g)
 
     cbuilder = genc.CStandaloneBuilder(t, main, t.config)
@@ -145,7 +186,17 @@
 
                     'fail\n'
                     'match\n'
-                    'match\n')
+                    'match\n'
+
+                    '-----\n'   # null
+                    '-----\n'   # instance of A
+                    'B----\n'   # instance of B
+                    'B----\n'   # instance of C
+                    '-D---\n'
+                    '--A--\n'
+                    '---S-\n'
+                    '----U\n'
+                    )
 
 
 def test_guards_translated_with_gctypeptr():
diff --git a/rpython/jit/metainterp/optimize.py b/rpython/jit/metainterp/optimize.py
--- a/rpython/jit/metainterp/optimize.py
+++ b/rpython/jit/metainterp/optimize.py
@@ -12,3 +12,7 @@
         debug_print(msg)
         debug_stop("jit-abort")
         self.msg = msg
+
+class SpeculativeError(JitException):
+    """Raised when speculative heap access would be ill-typed,
+    which should only occur when optimizing the unrolled loop."""
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
@@ -3,6 +3,7 @@
      ResOperation
 from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
 from rpython.jit.metainterp.optimizeopt.shortpreamble import PreambleOp
+from rpython.jit.metainterp.optimize import SpeculativeError
 
 
 class RecentPureOps(object):
@@ -93,6 +94,7 @@
                     break
             else:
                 # all constant arguments: constant-fold away
+                self.protect_speculative_operation(op)
                 resbox = self.optimizer.constant_fold(op)
                 # note that INT_xxx_OVF is not done from here, and the
                 # overflows in the INT_xxx operations are ignored
@@ -117,6 +119,59 @@
         if nextop:
             self.emit_operation(nextop)
 
+    def protect_speculative_operation(self, op):
+        """When constant-folding a pure operation that reads memory from
+        a gcref, make sure that the gcref is non-null and of a valid type.
+        Otherwise, raise SpeculativeError.  This should only occur when
+        unrolling and optimizing the unrolled loop.  Note that if
+        cpu.supports_guard_gc_type is false, we can't really do this
+        check at all, but then we don't unroll in that case.
+        """
+        opnum = op.getopnum()
+        cpu = self.optimizer.cpu
+
+        if   (opnum == rop.GETFIELD_GC_PURE_I or
+              opnum == rop.GETFIELD_GC_PURE_R or
+              opnum == rop.GETFIELD_GC_PURE_F):
+            fielddescr = op.getdescr()
+            ref = self.get_constant_box(op.getarg(0)).getref_base()
+            cpu.protect_speculative_field(ref, fielddescr)
+            return
+
+        elif (opnum == rop.GETARRAYITEM_GC_PURE_I or
+              opnum == rop.GETARRAYITEM_GC_PURE_R or
+              opnum == rop.GETARRAYITEM_GC_PURE_F or
+              opnum == rop.ARRAYLEN_GC):
+            arraydescr = op.getdescr()
+            array = self.get_constant_box(op.getarg(0)).getref_base()
+            cpu.protect_speculative_array(array, arraydescr)
+            if opnum == rop.ARRAYLEN_GC:
+                return
+            arraylength = cpu.bh_arraylen_gc(array, arraydescr)
+
+        elif (opnum == rop.STRGETITEM or
+              opnum == rop.STRLEN):
+            string = self.get_constant_box(op.getarg(0)).getref_base()
+            cpu.protect_speculative_string(string)
+            if opnum == rop.STRLEN:
+                return
+            arraylength = cpu.bh_strlen(string)
+
+        elif (opnum == rop.UNICODEGETITEM or
+              opnum == rop.UNICODELEN):
+            unicode = self.get_constant_box(op.getarg(0)).getref_base()
+            cpu.protect_speculative_unicode(unicode)
+            if opnum == rop.UNICODELEN:
+                return
+            arraylength = cpu.bh_unicodelen(unicode)
+
+        else:
+            return
+
+        index = self.get_constant_box(op.getarg(1)).getint()
+        if not (0 <= index < arraylength):
+            raise SpeculativeError
+
     def getrecentops(self, opnum):
         if rop._OVF_FIRST <= opnum <= rop._OVF_LAST:
             opnum = opnum - rop._OVF_FIRST
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_optimizeopt.py
@@ -9028,7 +9028,6 @@
         self.optimize_loop(ops, expected)
 
     def test_unroll_pure_on_bogus_object_1(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
@@ -9037,22 +9036,20 @@
         i3 = int_sub(i1, 1)
         jump(NULL, i3)
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_2(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
         guard_true(i2) []
         getfield_gc_pure_i(p0, descr=valuedescr)
         i3 = int_sub(i1, 1)
-        jump(ConstPtr(myptr3), i3)
-        """
-        self.optimize_loop(ops, ops)
+        jump(ConstPtr(myptr4), i3)
+        """
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_3(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
@@ -9061,10 +9058,9 @@
         i3 = int_sub(i1, 1)
         jump(NULL, i3)
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_4(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
@@ -9073,10 +9069,9 @@
         i3 = int_sub(i1, 1)
         jump(ConstPtr(myptr3), i3)
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_5(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
@@ -9085,10 +9080,9 @@
         i3 = int_sub(i1, 1)
         jump(ConstPtr(arrayref), i3)     # too short, length < 126!
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_6(self):
-        py.test.skip("FIXME")
         ops = """
         [i0, i1]
         i2 = int_gt(i1, 0)
@@ -9097,10 +9091,9 @@
         i3 = int_sub(i1, 1)
         jump(125, i3)     # arrayref is too short, length < 126!
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_7(self):
-        py.test.skip("FIXME")
         ops = """
         [i0, i1]
         i2 = int_gt(i1, 0)
@@ -9109,10 +9102,9 @@
         i3 = int_sub(i1, 1)
         jump(-1, i3)     # cannot access array item -1!
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_8(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
@@ -9121,10 +9113,9 @@
         i3 = int_sub(i1, 1)
         jump(NULL, i3)
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_9(self):
-        py.test.skip("FIXME")
         ops = """
         [p0, i1]
         i2 = int_gt(i1, 0)
@@ -9133,10 +9124,20 @@
         i3 = int_sub(i1, 1)
         jump(ConstPtr(myptr), i3)    # not a string at all
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
+
+    def test_unroll_pure_on_bogus_object_9_unicode(self):
+        ops = """
+        [p0, i1]
+        i2 = int_gt(i1, 0)
+        guard_true(i2) []
+        i4 = unicodegetitem(p0, 125)
+        i3 = int_sub(i1, 1)
+        jump(ConstPtr(myptr), i3)    # not a unicode at all
+        """
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_10(self):
-        py.test.skip("FIXME")
         ops = """
         [i0, i1]
         i2 = int_gt(i1, 0)
@@ -9145,10 +9146,9 @@
         i3 = int_sub(i1, 1)
         jump(125, i3)    # string is too short!
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
     def test_unroll_pure_on_bogus_object_11(self):
-        py.test.skip("FIXME")
         ops = """
         [i0, i1]
         i2 = int_gt(i1, 0)
@@ -9157,7 +9157,7 @@
         i3 = int_sub(i1, 1)
         jump(-1, i3)    # cannot access character -1!
         """
-        self.optimize_loop(ops, ops)
+        py.test.raises(InvalidLoop, self.optimize_loop, ops, ops)
 
 
 class TestLLtype(OptimizeOptTest, LLtypeMixin):
diff --git a/rpython/jit/metainterp/optimizeopt/test/test_util.py b/rpython/jit/metainterp/optimizeopt/test/test_util.py
--- a/rpython/jit/metainterp/optimizeopt/test/test_util.py
+++ b/rpython/jit/metainterp/optimizeopt/test/test_util.py
@@ -139,7 +139,10 @@
     myptr2 = lltype.cast_opaque_ptr(llmemory.GCREF, mynode2)
     mynode3 = lltype.malloc(NODE2)
     mynode3.parent.parent.typeptr = node_vtable2
-    myptr3 = lltype.cast_opaque_ptr(llmemory.GCREF, mynode3)
+    myptr3 = lltype.cast_opaque_ptr(llmemory.GCREF, mynode3)   # a NODE2
+    mynode4 = lltype.malloc(NODE3)
+    mynode4.parent.typeptr = node_vtable3
+    myptr4 = lltype.cast_opaque_ptr(llmemory.GCREF, mynode4)   # a NODE3
     nullptr = lltype.nullptr(llmemory.GCREF.TO)
     #nodebox2 = InputArgRef(lltype.cast_opaque_ptr(llmemory.GCREF, node2))
     nodesize = cpu.sizeof(NODE, node_vtable)
diff --git a/rpython/jit/metainterp/optimizeopt/unroll.py b/rpython/jit/metainterp/optimizeopt/unroll.py
--- a/rpython/jit/metainterp/optimizeopt/unroll.py
+++ b/rpython/jit/metainterp/optimizeopt/unroll.py
@@ -4,7 +4,7 @@
 from rpython.jit.metainterp.optimizeopt.shortpreamble import ShortBoxes,\
      ShortPreambleBuilder, ExtendedShortPreambleBuilder, PreambleOp
 from rpython.jit.metainterp.optimizeopt import info, intutils
-from rpython.jit.metainterp.optimize import InvalidLoop
+from rpython.jit.metainterp.optimize import InvalidLoop, SpeculativeError
 from rpython.jit.metainterp.optimizeopt.optimizer import Optimizer,\
      Optimization, LoopInfo, MININT, MAXINT, BasicLoopInfo
 from rpython.jit.metainterp.optimizeopt.vstring import StrPtrInfo
@@ -144,9 +144,12 @@
             raise InvalidLoop("Cannot import state, virtual states don't match")
         self.potential_extra_ops = {}
         self.optimizer.init_inparg_dict_from(label_args)
-        info, _ = self.optimizer.propagate_all_forward(
-            start_label.getarglist()[:], ops, call_pure_results, False,
-            flush=False)
+        try:
+            info, _ = self.optimizer.propagate_all_forward(
+                start_label.getarglist()[:], ops, call_pure_results, False,
+                flush=False)
+        except SpeculativeError:
+            raise InvalidLoop("Speculative heap access would be ill-typed")
         label_op = ResOperation(rop.LABEL, label_args, start_label.getdescr())
         for a in end_jump.getarglist():
             self.optimizer.force_box_for_end_of_preamble(


More information about the pypy-commit mailing list