Author: Armin Rigo <[email protected]>
Branch: 
Changeset: r73886:6f51f0dc9cb0
Date: 2014-10-10 18:09 +0200
http://bitbucket.org/pypy/pypy/changeset/6f51f0dc9cb0/

Log:    Fix for RPython code like '[0] * n', which was incorrectly jitted
        with 'new_array', i.e. the version that doesn't fill the result with
        zeroes.

diff --git a/rpython/jit/codewriter/jtransform.py 
b/rpython/jit/codewriter/jtransform.py
--- a/rpython/jit/codewriter/jtransform.py
+++ b/rpython/jit/codewriter/jtransform.py
@@ -1653,41 +1653,34 @@
         self._get_list_nonneg_canraise_flags(op)
 
     def _get_initial_newlist_length(self, op, args):
-        # normalize number of arguments to the 'newlist' function
-        if len(args) > 1:
-            v_default = args[1]     # initial value: must be 0 or NULL
-            ARRAY = deref(op.result.concretetype)
-            if (not isinstance(v_default, Constant) or
-                v_default.value != arrayItem(ARRAY)._defl()):
-                raise NotSupported("variable or non-null initial value")
-        if len(args) >= 1:
-            return args[0]
+        assert len(args) <= 1
+        if len(args) == 1:
+            v_length = args[0]
+            assert v_length.concretetype is lltype.Signed
+            return v_length
         else:
             return Constant(0, lltype.Signed)     # length: default to 0
 
     # ---------- fixed lists ----------
 
     def do_fixed_newlist(self, op, args, arraydescr):
+        # corresponds to rtyper.lltypesystem.rlist.newlist:
+        # the items may be uninitialized.
         v_length = self._get_initial_newlist_length(op, args)
-        assert v_length.concretetype is lltype.Signed
-        ops = []
-        if isinstance(v_length, Constant):
-            if v_length.value >= 0:
-                v = v_length
-            else:
-                v = Constant(0, lltype.Signed)
-        else:
-            v = Variable('new_length')
-            v.concretetype = lltype.Signed
-            ops.append(SpaceOperation('int_force_ge_zero', [v_length], v))
         ARRAY = op.result.concretetype.TO
         if ((isinstance(ARRAY.OF, lltype.Ptr) and ARRAY.OF._needsgc()) or
                isinstance(ARRAY.OF, lltype.Struct)):
             opname = 'new_array_clear'
         else:
             opname = 'new_array'
-        ops.append(SpaceOperation(opname, [v, arraydescr], op.result))
-        return ops
+        return SpaceOperation(opname, [v_length, arraydescr], op.result)
+
+    def do_fixed_newlist_clear(self, op, args, arraydescr):
+        # corresponds to rtyper.rlist.ll_alloc_and_clear:
+        # needs to clear the items.
+        v_length = self._get_initial_newlist_length(op, args)
+        return SpaceOperation('new_array_clear', [v_length, arraydescr],
+                              op.result)
 
     def do_fixed_list_len(self, op, args, arraydescr):
         if args[0] in self.vable_array_vars:     # virtualizable array
@@ -1757,6 +1750,14 @@
                                arraydescr],
                               op.result)
 
+    def do_resizable_newlist_clear(self, op, args, arraydescr, lengthdescr,
+                                   itemsdescr, structdescr):
+        v_length = self._get_initial_newlist_length(op, args)
+        return SpaceOperation('newlist_clear',
+                              [v_length, structdescr, lengthdescr, itemsdescr,
+                               arraydescr],
+                              op.result)
+
     def do_resizable_newlist_hint(self, op, args, arraydescr, lengthdescr,
                                   itemsdescr, structdescr):
         v_hint = self._get_initial_newlist_length(op, args)
diff --git a/rpython/jit/codewriter/support.py 
b/rpython/jit/codewriter/support.py
--- a/rpython/jit/codewriter/support.py
+++ b/rpython/jit/codewriter/support.py
@@ -180,11 +180,11 @@
     return LIST.ll_newlist(0)
 def _ll_1_newlist(LIST, count):
     return LIST.ll_newlist(count)
-def _ll_2_newlist(LIST, count, item):
-    return rlist.ll_alloc_and_set(LIST, count, item)
 _ll_0_newlist.need_result_type = True
 _ll_1_newlist.need_result_type = True
-_ll_2_newlist.need_result_type = True
+
+_ll_1_newlist_clear = rlist._ll_alloc_and_clear
+_ll_1_newlist_clear.need_result_type = True
 
 def _ll_1_newlist_hint(LIST, hint):
     return LIST.ll_newlist_hint(hint)
diff --git a/rpython/jit/codewriter/test/test_codewriter.py 
b/rpython/jit/codewriter/test/test_codewriter.py
--- a/rpython/jit/codewriter/test/test_codewriter.py
+++ b/rpython/jit/codewriter/test/test_codewriter.py
@@ -232,18 +232,3 @@
     assert 'setarrayitem_raw_i' in s
     assert 'getarrayitem_raw_i' in s
     assert 'residual_call_ir_v $<* fn _ll_1_raw_free__arrayPtr>' in s
-
-def test_newlist_negativ():
-    def f(n):
-        l = [0] * n
-        return len(l)
-
-    rtyper = support.annotate(f, [-1])
-    jitdriver_sd = FakeJitDriverSD(rtyper.annotator.translator.graphs[0])
-    cw = CodeWriter(FakeCPU(rtyper), [jitdriver_sd])
-    graphs = cw.find_all_graphs(FakePolicy())
-    backend_optimizations(rtyper.annotator.translator, graphs=graphs)
-    cw.make_jitcodes(verbose=True)
-    s = jitdriver_sd.mainjitcode.dump()
-    assert 'int_force_ge_zero' in s
-    assert 'new_array' in s
diff --git a/rpython/jit/codewriter/test/test_list.py 
b/rpython/jit/codewriter/test/test_list.py
--- a/rpython/jit/codewriter/test/test_list.py
+++ b/rpython/jit/codewriter/test/test_list.py
@@ -87,20 +87,10 @@
                  """new_array $0, <ArrayDescr> -> %r0""")
     builtin_test('newlist', [Constant(5, lltype.Signed)], FIXEDLIST,
                  """new_array $5, <ArrayDescr> -> %r0""")
-    builtin_test('newlist', [Constant(-2, lltype.Signed)], FIXEDLIST,
-                 """new_array $0, <ArrayDescr> -> %r0""")
     builtin_test('newlist', [varoftype(lltype.Signed)], FIXEDLIST,
-                 """int_force_ge_zero %i0 -> %i1\n"""
-                 """new_array %i1, <ArrayDescr> -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(0, lltype.Signed)], FIXEDLIST,
-                 """new_array $5, <ArrayDescr> -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(1, lltype.Signed)], FIXEDLIST,
-                 NotSupported)
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             varoftype(lltype.Signed)], FIXEDLIST,
-                 NotSupported)
+                 """new_array %i0, <ArrayDescr> -> %r0""")
+    builtin_test('newlist_clear', [Constant(5, lltype.Signed)], FIXEDLIST,
+                 """new_array_clear $5, <ArrayDescr> -> %r0""")
     builtin_test('newlist', [], FIXEDPTRLIST,
                  """new_array_clear $0, <ArrayDescr> -> %r0""")
 
@@ -179,15 +169,8 @@
                  """newlist $5, """+alldescrs+""" -> %r0""")
     builtin_test('newlist', [varoftype(lltype.Signed)], VARLIST,
                  """newlist %i0, """+alldescrs+""" -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(0, lltype.Signed)], VARLIST,
-                 """newlist $5, """+alldescrs+""" -> %r0""")
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             Constant(1, lltype.Signed)], VARLIST,
-                 NotSupported)
-    builtin_test('newlist', [Constant(5, lltype.Signed),
-                             varoftype(lltype.Signed)], VARLIST,
-                 NotSupported)
+    builtin_test('newlist_clear', [Constant(5, lltype.Signed)], VARLIST,
+                 """newlist_clear $5, """+alldescrs+""" -> %r0""")
 
 def test_resizable_getitem():
     builtin_test('list.getitem/NONNEG',
diff --git a/rpython/jit/metainterp/blackhole.py 
b/rpython/jit/metainterp/blackhole.py
--- a/rpython/jit/metainterp/blackhole.py
+++ b/rpython/jit/metainterp/blackhole.py
@@ -1017,6 +1017,15 @@
         return result
 
     @arguments("cpu", "i", "d", "d", "d", "d", returns="r")
+    def bhimpl_newlist_clear(cpu, length, structdescr, lengthdescr,
+                             itemsdescr, arraydescr):
+        result = cpu.bh_new(structdescr)
+        cpu.bh_setfield_gc_i(result, length, lengthdescr)
+        items = cpu.bh_new_array_clear(length, arraydescr)
+        cpu.bh_setfield_gc_r(result, items, itemsdescr)
+        return result
+
+    @arguments("cpu", "i", "d", "d", "d", "d", returns="r")
     def bhimpl_newlist_hint(cpu, lengthhint, structdescr, lengthdescr,
                             itemsdescr, arraydescr):
         result = cpu.bh_new(structdescr)
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
@@ -522,6 +522,15 @@
         return sbox
 
     @arguments("box", "descr", "descr", "descr", "descr")
+    def opimpl_newlist_clear(self, sizebox, structdescr, lengthdescr,
+                             itemsdescr, arraydescr):
+        sbox = self.opimpl_new(structdescr)
+        self._opimpl_setfield_gc_any(sbox, sizebox, lengthdescr)
+        abox = self.opimpl_new_array_clear(sizebox, arraydescr)
+        self._opimpl_setfield_gc_any(sbox, abox, itemsdescr)
+        return sbox
+
+    @arguments("box", "descr", "descr", "descr", "descr")
     def opimpl_newlist_hint(self, sizehintbox, structdescr, lengthdescr,
                             itemsdescr, arraydescr):
         sbox = self.opimpl_new(structdescr)
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
@@ -12,7 +12,7 @@
     AssertGreenFailed, unroll_safe, current_trace_length, look_inside_iff,
     isconstant, isvirtual, set_param, record_known_class)
 from rpython.rlib.longlong2float import float2longlong, longlong2float
-from rpython.rlib.rarithmetic import ovfcheck, is_valid_int
+from rpython.rlib.rarithmetic import ovfcheck, is_valid_int, int_force_ge_zero
 from rpython.rtyper.lltypesystem import lltype, rffi
 
 
@@ -4111,3 +4111,11 @@
 
         res = self.meta_interp(f, [10])
         assert res == 42
+
+    def test_int_force_ge_zero(self):
+        def f(n):
+            return int_force_ge_zero(n)
+        res = self.interp_operations(f, [42])
+        assert res == 42
+        res = self.interp_operations(f, [-42])
+        assert res == 0
diff --git a/rpython/jit/metainterp/test/test_list.py 
b/rpython/jit/metainterp/test/test_list.py
--- a/rpython/jit/metainterp/test/test_list.py
+++ b/rpython/jit/metainterp/test/test_list.py
@@ -414,3 +414,13 @@
         res = self.meta_interp(f, [10])
         assert res == 0
         self.check_resops(call=0, cond_call=2)
+
+    def test_zero_init_resizable(self):
+        def f(n):
+            l = [0] * n
+            l.append(123)
+            return len(l) + l[0] + l[1] + l[2] + l[3] + l[4] + l[5] + l[6]
+
+        res = self.interp_operations(f, [10], listops=True, inline=True)
+        assert res == 11
+        self.check_operations_history(new_array_clear=1)
diff --git a/rpython/rlib/rarithmetic.py b/rpython/rlib/rarithmetic.py
--- a/rpython/rlib/rarithmetic.py
+++ b/rpython/rlib/rarithmetic.py
@@ -635,6 +635,12 @@
         assert n <= p
     return llop.int_between(lltype.Bool, n, m, p)
 
+def int_force_ge_zero(n):
+    """ The JIT special-cases this too. """
+    from rpython.rtyper.lltypesystem import lltype
+    from rpython.rtyper.lltypesystem.lloperation import llop
+    return llop.int_force_ge_zero(lltype.Signed, n)
+
 @objectmodel.specialize.ll()
 def byteswap(arg):
     """ Convert little->big endian and the opposite
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -216,6 +216,7 @@
     'int_xor':              LLOp(canfold=True),
 
     'int_between':          LLOp(canfold=True),   # a <= b < c
+    'int_force_ge_zero':    LLOp(canfold=True),   # 0 if a < 0 else a
 
     'int_add_ovf':          LLOp(canraise=(OverflowError,), tryfold=True),
     'int_add_nonneg_ovf':   LLOp(canraise=(OverflowError,), tryfold=True),
diff --git a/rpython/rtyper/lltypesystem/opimpl.py 
b/rpython/rtyper/lltypesystem/opimpl.py
--- a/rpython/rtyper/lltypesystem/opimpl.py
+++ b/rpython/rtyper/lltypesystem/opimpl.py
@@ -225,6 +225,12 @@
     assert lltype.typeOf(c) is lltype.Signed
     return a <= b < c
 
+def op_int_force_ge_zero(a):
+    assert lltype.typeOf(a) is lltype.Signed
+    if a < 0:
+        return 0
+    return a
+
 def op_int_and(x, y):
     if not is_valid_int(x):
         from rpython.rtyper.lltypesystem import llgroup
diff --git a/rpython/rtyper/rlist.py b/rpython/rtyper/rlist.py
--- a/rpython/rtyper/rlist.py
+++ b/rpython/rtyper/rlist.py
@@ -2,9 +2,10 @@
 from rpython.flowspace.model import Constant
 from rpython.rlib import rgc, jit, types
 from rpython.rlib.debug import ll_assert
-from rpython.rlib.objectmodel import malloc_zero_filled, enforceargs
+from rpython.rlib.objectmodel import malloc_zero_filled, enforceargs, 
specialize
 from rpython.rlib.signature import signature
 from rpython.rlib.rarithmetic import ovfcheck, widen, r_uint, intmask
+from rpython.rlib.rarithmetic import int_force_ge_zero
 from rpython.rtyper.annlowlevel import ADTInterface
 from rpython.rtyper.error import TyperError
 from rpython.rtyper.lltypesystem.lltype import typeOf, Ptr, Void, Signed, Bool
@@ -474,15 +475,8 @@
 #  when we compute "ll_length() + 1".
 
 
-# jit note: this is normally special-cased by the oopspec,
-# but if item != const(0), then the special-casing fails and
-# we fall back to the look_inside_iff.
[email protected]_inside_iff(lambda LIST, count, item: jit.isconstant(count) and count 
< 137)
[email protected]("newlist(count, item)")
-def ll_alloc_and_set(LIST, count, item):
-    if count < 0:
-        count = 0
-    l = LIST.ll_newlist(count)
+def _ll_zero_or_null(item):
+    # Check if 'item' is zero/null, or not.
     T = typeOf(item)
     if T is Char or T is UniChar:
         check = ord(item)
@@ -490,13 +484,60 @@
         check = widen(item)
     else:
         check = item
-    # as long as malloc is known to zero the allocated memory avoid zeroing
-    # twice
-    if jit.we_are_jitted() or (not malloc_zero_filled) or check:
-        i = 0
-        while i < count:
-            l.ll_setitem_fast(i, item)
-            i += 1
+    return not check
+
[email protected]()
+def _null_of_type(T):
+    return T._defl()
+
+def ll_alloc_and_set(LIST, count, item):
+    count = int_force_ge_zero(count)
+    if jit.we_are_jitted():
+        return _ll_alloc_and_set_jit(LIST, count, item)
+    else:
+        return _ll_alloc_and_set_nojit(LIST, count, item)
+
+def _ll_alloc_and_set_nojit(LIST, count, item):
+    l = LIST.ll_newlist(count)
+    if malloc_zero_filled and _ll_zero_or_null(item):
+        return l
+    i = 0
+    while i < count:
+        l.ll_setitem_fast(i, item)
+        i += 1
+    return l
+
+def _ll_alloc_and_set_jit(LIST, count, item):
+    if _ll_zero_or_null(item):
+        # 'item' is zero/null.  Do the list allocation with the
+        # function _ll_alloc_and_clear(), which the JIT knows about.
+        return _ll_alloc_and_clear(LIST, count)
+    else:
+        # 'item' is not zero/null.  Do the list allocation with the
+        # function _ll_alloc_and_set_nonnull().  That function has
+        # a JIT marker to unroll it, but only if the 'count' is
+        # a not-too-large constant.
+        return _ll_alloc_and_set_nonnull(LIST, count, item)
+
[email protected]("newlist_clear(count)")
+def _ll_alloc_and_clear(LIST, count):
+    l = LIST.ll_newlist(count)
+    if malloc_zero_filled:
+        return l
+    zeroitem = _null_of_type(LIST.ITEM)
+    i = 0
+    while i < count:
+        l.ll_setitem_fast(i, zeroitem)
+        i += 1
+    return l
+
[email protected]_inside_iff(lambda LIST, count, item: jit.isconstant(count) and count 
< 137)
+def _ll_alloc_and_set_nonnull(LIST, count, item):
+    l = LIST.ll_newlist(count)
+    i = 0
+    while i < count:
+        l.ll_setitem_fast(i, item)
+        i += 1
     return l
 
 
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to