Author: Matti Picus <[email protected]>
Branch: cpyext-ext
Changeset: r84888:8939df11b5d2
Date: 2016-06-02 18:33 +0300
http://bitbucket.org/pypy/pypy/changeset/8939df11b5d2/

Log:    merge nonmovable-list into branch

diff --git a/rpython/memory/gc/base.py b/rpython/memory/gc/base.py
--- a/rpython/memory/gc/base.py
+++ b/rpython/memory/gc/base.py
@@ -184,7 +184,7 @@
     def can_move(self, addr):
         return False
 
-    def malloc_fixedsize_nonmovable(self, typeid):
+    def malloc_fixed_or_varsize_nonmovable(self, typeid, length):
         raise MemoryError
 
     def pin(self, addr):
diff --git a/rpython/memory/gc/incminimark.py b/rpython/memory/gc/incminimark.py
--- a/rpython/memory/gc/incminimark.py
+++ b/rpython/memory/gc/incminimark.py
@@ -718,8 +718,9 @@
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
-    def malloc_fixedsize_nonmovable(self, typeid):
-        obj = self.external_malloc(typeid, 0, alloc_young=True)
+    def malloc_fixed_or_varsize_nonmovable(self, typeid, length):
+        # length==0 for fixedsize
+        obj = self.external_malloc(typeid, length, alloc_young=True)
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
diff --git a/rpython/memory/gc/minimark.py b/rpython/memory/gc/minimark.py
--- a/rpython/memory/gc/minimark.py
+++ b/rpython/memory/gc/minimark.py
@@ -628,8 +628,9 @@
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
-    def malloc_fixedsize_nonmovable(self, typeid):
-        obj = self.external_malloc(typeid, 0, alloc_young=True)
+    def malloc_fixed_or_varsize_nonmovable(self, typeid, length):
+        # length==0 for fixedsize
+        obj = self.external_malloc(typeid, length, alloc_young=True)
         return llmemory.cast_adr_to_ptr(obj, llmemory.GCREF)
 
 
diff --git a/rpython/memory/gctransform/framework.py 
b/rpython/memory/gctransform/framework.py
--- a/rpython/memory/gctransform/framework.py
+++ b/rpython/memory/gctransform/framework.py
@@ -557,9 +557,10 @@
                                              getfn(func,
                                                    [SomeAddress()],
                                                    annmodel.s_None)
-        self.malloc_nonmovable_ptr = getfn(GCClass.malloc_fixedsize_nonmovable,
-                                           [s_gc, s_typeid16],
-                                           s_gcref)
+        self.malloc_nonmovable_ptr = getfn(
+            GCClass.malloc_fixed_or_varsize_nonmovable,
+            [s_gc, s_typeid16, annmodel.SomeInteger()],
+            s_gcref)
 
         self.register_finalizer_ptr = getfn(GCClass.register_finalizer,
                                             [s_gc,
@@ -800,12 +801,16 @@
         c_has_light_finalizer = rmodel.inputconst(lltype.Bool,
                                                   has_light_finalizer)
 
+        is_varsize = op.opname.endswith('_varsize') or flags.get('varsize')
+
         if flags.get('nonmovable'):
-            assert op.opname == 'malloc'
-            assert not flags.get('varsize')
+            if not is_varsize:
+                v_length = rmodel.inputconst(lltype.Signed, 0)
+            else:
+                v_length = op.args[-1]
             malloc_ptr = self.malloc_nonmovable_ptr
-            args = [self.c_const_gc, c_type_id]
-        elif not op.opname.endswith('_varsize') and not flags.get('varsize'):
+            args = [self.c_const_gc, c_type_id, v_length]
+        elif not is_varsize:
             zero = flags.get('zero', False)
             if (self.malloc_fast_ptr is not None and
                 not c_has_finalizer.value and
diff --git a/rpython/rlib/rgc.py b/rpython/rlib/rgc.py
--- a/rpython/rlib/rgc.py
+++ b/rpython/rlib/rgc.py
@@ -1001,3 +1001,259 @@
     def specialize_call(self, hop):
         hop.exception_cannot_occur()
         return hop.genop('gc_gettypeid', hop.args_v, resulttype=lltype.Signed)
+
+# ____________________________________________________________
+
+
+class _rawptr_missing_item(object):
+    pass
+_rawptr_missing_item = _rawptr_missing_item()
+
+
+class _ResizableListSupportingRawPtr(list):
+    """Calling this class is a no-op after translation.
+
+    Before translation, it returns a new instance of
+    _ResizableListSupportingRawPtr, on which
+    rgc.nonmoving_raw_ptr_for_resizable_list() might be
+    used if needed.  For now, only supports lists of chars.
+    """
+    __slots__ = ('_raw_items',)   # either None or a rffi.CCHARP
+
+    def __init__(self, lst):
+        self._raw_items = None
+        self.__from_list(lst)
+
+    def __resize(self):
+        """Called before an operation changes the size of the list"""
+        if self._raw_items is not None:
+            list.__init__(self, self.__as_list())
+            self._raw_items = None
+
+    def __from_list(self, lst):
+        """Initialize the list from a copy of the list 'lst'."""
+        assert isinstance(lst, list)
+        for x in lst:
+            assert isinstance(x, str) and len(x) == 1
+        if self is lst:
+            return
+        if len(self) != len(lst):
+            self.__resize()
+        if self._raw_items is None:
+            list.__init__(self, lst)
+        else:
+            assert len(self) == self._raw_items._obj.getlength() == len(lst)
+            for i in range(len(self)):
+                self._raw_items[i] = lst[i]
+
+    def __as_list(self):
+        """Return a list (the same or a different one) which contains the
+        items in the regular way."""
+        if self._raw_items is None:
+            return self
+        length = self._raw_items._obj.getlength()
+        assert length == len(self)
+        return [self._raw_items[i] for i in range(length)]
+
+    def __getitem__(self, index):
+        if self._raw_items is None:
+            return list.__getitem__(self, index)
+        if index < 0:
+            index += len(self)
+        if not (0 <= index < len(self)):
+            raise IndexError
+        return self._raw_items[index]
+
+    def __setitem__(self, index, new):
+        if self._raw_items is None:
+            return list.__setitem__(self, index, new)
+        if index < 0:
+            index += len(self)
+        if not (0 <= index < len(self)):
+            raise IndexError
+        self._raw_items[index] = new
+
+    def __delitem__(self, index):
+        self.__resize()
+        list.__delitem__(self, index)
+
+    def __getslice__(self, i, j):
+        return list.__getslice__(self.__as_list(), i, j)
+
+    def __setslice__(self, i, j, new):
+        lst = self.__as_list()
+        list.__setslice__(lst, i, j, new)
+        self.__from_list(lst)
+
+    def __delslice__(self, i, j):
+        lst = self.__as_list()
+        list.__delslice__(lst, i, j)
+        self.__from_list(lst)
+
+    def __iter__(self):
+        try:
+            i = 0
+            while True:
+                yield self[i]
+                i += 1
+        except IndexError:
+            pass
+
+    def __reversed__(self):
+        i = len(self)
+        while i > 0:
+            i -= 1
+            yield self[i]
+
+    def __contains__(self, item):
+        return list.__contains__(self.__as_list(), item)
+
+    def __add__(self, other):
+        if isinstance(other, _ResizableListSupportingRawPtr):
+            other = other.__as_list()
+        return list.__add__(self.__as_list(), other)
+
+    def __radd__(self, other):
+        if isinstance(other, _ResizableListSupportingRawPtr):
+            other = other.__as_list()
+        return list.__add__(other, self.__as_list())
+
+    def __iadd__(self, other):
+        self.__resize()
+        return list.__iadd__(self, other)
+
+    def __eq__(self, other):
+        return list.__eq__(self.__as_list(), other)
+    def __ne__(self, other):
+        return list.__ne__(self.__as_list(), other)
+    def __ge__(self, other):
+        return list.__ge__(self.__as_list(), other)
+    def __gt__(self, other):
+        return list.__gt__(self.__as_list(), other)
+    def __le__(self, other):
+        return list.__le__(self.__as_list(), other)
+    def __lt__(self, other):
+        return list.__lt__(self.__as_list(), other)
+
+    def __mul__(self, other):
+        return list.__mul__(self.__as_list(), other)
+
+    def __rmul__(self, other):
+        return list.__mul__(self.__as_list(), other)
+
+    def __imul__(self, other):
+        self.__resize()
+        return list.__imul__(self, other)
+
+    def __repr__(self):
+        return '_ResizableListSupportingRawPtr(%s)' % (
+            list.__repr__(self.__as_list()),)
+
+    def append(self, object):
+        self.__resize()
+        return list.append(self, object)
+
+    def count(self, value):
+        return list.count(self.__as_list(), value)
+
+    def extend(self, iterable):
+        self.__resize()
+        return list.extend(self, iterable)
+
+    def index(self, value, *start_stop):
+        return list.index(self.__as_list(), value, *start_stop)
+
+    def insert(self, index, object):
+        self.__resize()
+        return list.insert(self, index, object)
+
+    def pop(self, *opt_index):
+        self.__resize()
+        return list.pop(self, *opt_index)
+
+    def remove(self, value):
+        self.__resize()
+        return list.remove(self, value)
+
+    def reverse(self):
+        lst = self.__as_list()
+        list.reverse(lst)
+        self.__from_list(lst)
+
+    def sort(self, *args, **kwds):
+        lst = self.__as_list()
+        list.sort(lst, *args, **kwds)
+        self.__from_list(lst)
+
+    def _nonmoving_raw_ptr_for_resizable_list(self):
+        if self._raw_items is None:
+            existing_items = list(self)
+            from rpython.rtyper.lltypesystem import lltype, rffi
+            self._raw_items = lltype.malloc(rffi.CCHARP.TO, len(self),
+                                           flavor='raw', immortal=True)
+            self.__from_list(existing_items)
+            assert self._raw_items is not None
+        return self._raw_items
+
+def resizable_list_supporting_raw_ptr(lst):
+    return _ResizableListSupportingRawPtr(lst)
+
+def nonmoving_raw_ptr_for_resizable_list(lst):
+    assert isinstance(lst, _ResizableListSupportingRawPtr)
+    return lst._nonmoving_raw_ptr_for_resizable_list()
+
+
+def _check_resizable_list_of_chars(s_list):
+    from rpython.annotator import model as annmodel
+    from rpython.rlib import debug
+    if annmodel.s_None.contains(s_list):
+        return    # "None", will likely be generalized later
+    if not isinstance(s_list, annmodel.SomeList):
+        raise Exception("not a list, got %r" % (s_list,))
+    if not isinstance(s_list.listdef.listitem.s_value,
+                      (annmodel.SomeChar, annmodel.SomeImpossibleValue)):
+        raise debug.NotAListOfChars
+    s_list.listdef.resize()    # must be resizable
+
+class Entry(ExtRegistryEntry):
+    _about_ = resizable_list_supporting_raw_ptr
+
+    def compute_result_annotation(self, s_list):
+        _check_resizable_list_of_chars(s_list)
+        return s_list
+
+    def specialize_call(self, hop):
+        hop.exception_cannot_occur()
+        return hop.inputarg(hop.args_r[0], 0)
+
+class Entry(ExtRegistryEntry):
+    _about_ = nonmoving_raw_ptr_for_resizable_list
+
+    def compute_result_annotation(self, s_list):
+        from rpython.rtyper.lltypesystem import lltype, rffi
+        from rpython.rtyper.llannotation import SomePtr
+        _check_resizable_list_of_chars(s_list)
+        return SomePtr(rffi.CCHARP)
+
+    def specialize_call(self, hop):
+        v_list = hop.inputarg(hop.args_r[0], 0)
+        hop.exception_cannot_occur()   # ignoring MemoryError
+        return hop.gendirectcall(ll_nonmovable_raw_ptr_for_resizable_list,
+                                 v_list)
+
+def ll_nonmovable_raw_ptr_for_resizable_list(ll_list):
+    from rpython.rtyper.lltypesystem import lltype, rffi
+    array = ll_list.items
+    if can_move(array):
+        length = ll_list.length
+        new_array = lltype.malloc(lltype.typeOf(ll_list).TO.items.TO, length,
+                                  nonmovable=True)
+        i = 0
+        while i < length:
+            new_array[i] = array[i]
+            i += 1
+        ll_list.items = new_array
+        array = new_array
+    ptr = lltype.direct_arrayitems(array)
+    # ptr is a Ptr(FixedSizeArray(Char, 1)).  Cast it to a rffi.CCHARP
+    return rffi.cast(rffi.CCHARP, ptr)
diff --git a/rpython/rlib/test/test_rgc.py b/rpython/rlib/test/test_rgc.py
--- a/rpython/rlib/test/test_rgc.py
+++ b/rpython/rlib/test/test_rgc.py
@@ -1,6 +1,6 @@
 from rpython.rtyper.test.test_llinterp import gengraph, interpret
 from rpython.rtyper.error import TyperError
-from rpython.rtyper.lltypesystem import lltype, llmemory
+from rpython.rtyper.lltypesystem import lltype, llmemory, rffi
 from rpython.rlib import rgc # Force registration of gc.collect
 import gc
 import py, sys
@@ -254,6 +254,153 @@
 
     assert typer.custom_trace_funcs == [(TP, trace_func)]
 
+def test_nonmoving_raw_ptr_for_resizable_list():
+    def f(n):
+        lst = ['a', 'b', 'c']
+        lst = rgc.resizable_list_supporting_raw_ptr(lst)
+        lst.append(chr(n))
+        assert lst[3] == chr(n)
+        assert lst[-1] == chr(n)
+        #
+        ptr = rgc.nonmoving_raw_ptr_for_resizable_list(lst)
+        assert lst[:] == ['a', 'b', 'c', chr(n)]
+        assert lltype.typeOf(ptr) == rffi.CCHARP
+        assert [ptr[i] for i in range(4)] == ['a', 'b', 'c', chr(n)]
+        #
+        lst[-3] = 'X'
+        assert ptr[1] == 'X'
+        ptr[2] = 'Y'
+        assert lst[-2] == 'Y'
+        #
+        addr = rffi.cast(lltype.Signed, ptr)
+        ptr = rffi.cast(rffi.CCHARP, addr)
+        rgc.collect()    # should not move lst.items
+        lst[-4] = 'g'
+        assert ptr[0] == 'g'
+        ptr[3] = 'H'
+        assert lst[-1] == 'H'
+        return lst
+    #
+    # direct untranslated run
+    lst = f(35)
+    assert isinstance(lst, rgc._ResizableListSupportingRawPtr)
+    #
+    # llinterp run
+    interpret(f, [35])
+    #
+    # compilation with the GC transformer
+    import subprocess
+    from rpython.translator.interactive import Translation
+    #
+    def main(argv):
+        f(len(argv))
+        print "OK!"
+        return 0
+    #
+    t = Translation(main, gc="incminimark")
+    t.disable(['backendopt'])
+    t.set_backend_extra_options(c_debug_defines=True)
+    exename = t.compile()
+    data = subprocess.check_output([str(exename), '.', '.', '.'])
+    assert data.strip().endswith('OK!')
+
+def test_ListSupportingRawPtr_direct():
+    lst = ['a', 'b', 'c']
+    lst = rgc.resizable_list_supporting_raw_ptr(lst)
+
+    def check_nonresizing():
+        assert lst[1] == lst[-2] == 'b'
+        lst[1] = 'X'
+        assert lst[1] == 'X'
+        lst[-1] = 'Y'
+        assert lst[1:3] == ['X', 'Y']
+        assert lst[-2:9] == ['X', 'Y']
+        lst[1:2] = 'B'
+        assert lst[:] == ['a', 'B', 'Y']
+        assert list(iter(lst)) == ['a', 'B', 'Y']
+        assert list(reversed(lst)) == ['Y', 'B', 'a']
+        assert 'B' in lst
+        assert 'b' not in lst
+        assert p[0] == 'a'
+        assert p[1] == 'B'
+        assert p[2] == 'Y'
+        assert lst + ['*'] == ['a', 'B', 'Y', '*']
+        assert ['*'] + lst == ['*', 'a', 'B', 'Y']
+        assert lst + lst == ['a', 'B', 'Y', 'a', 'B', 'Y']
+        base = ['8']
+        base += lst
+        assert base == ['8', 'a', 'B', 'Y']
+        assert lst == ['a', 'B', 'Y']
+        assert ['a', 'B', 'Y'] == lst
+        assert ['a', 'B', 'Z'] != lst
+        assert ['a', 'B', 'Z'] >  lst
+        assert ['a', 'B', 'Z'] >= lst
+        assert lst * 2 == ['a', 'B', 'Y', 'a', 'B', 'Y']
+        assert 2 * lst == ['a', 'B', 'Y', 'a', 'B', 'Y']
+        assert lst.count('B') == 1
+        assert lst.index('Y') == 2
+        lst.reverse()
+        assert lst == ['Y', 'B', 'a']
+        lst.sort()
+        assert lst == ['B', 'Y', 'a']
+        lst.sort(reverse=True)
+        assert lst == ['a', 'Y', 'B']
+        lst[1] = 'b'
+        lst[2] = 'c'
+        assert list(lst) == ['a', 'b', 'c']
+
+    p = lst
+    check_nonresizing()
+    assert lst._raw_items is None
+    lst._nonmoving_raw_ptr_for_resizable_list()
+    p = lst._raw_items
+    check_nonresizing()
+    assert lst._raw_items == p
+    assert p[0] == 'a'
+    assert p[1] == 'b'
+    assert p[2] == 'c'
+
+    def do_resizing_operation():
+        del lst[1]
+        yield ['a', 'c']
+
+        lst[:2] = ['X']
+        yield ['X', 'c']
+
+        del lst[:2]
+        yield ['c']
+
+        x = lst
+        x += ['t']
+        yield ['a', 'b', 'c', 't']
+
+        x = lst
+        x *= 3
+        yield ['a', 'b', 'c'] * 3
+
+        lst.append('f')
+        yield ['a', 'b', 'c', 'f']
+
+        lst.extend('fg')
+        yield ['a', 'b', 'c', 'f', 'g']
+
+        lst.insert(1, 'k')
+        yield ['a', 'k', 'b', 'c']
+
+        n = lst.pop(1)
+        assert n == 'b'
+        yield ['a', 'c']
+
+        lst.remove('c')
+        yield ['a', 'b']
+
+    assert lst == ['a', 'b', 'c']
+    for expect in do_resizing_operation():
+        assert lst == expect
+        assert lst._raw_items is None
+        lst = ['a', 'b', 'c']
+        lst = rgc.resizable_list_supporting_raw_ptr(lst)
+        lst._nonmoving_raw_ptr_for_resizable_list()
 
 # ____________________________________________________________
 
@@ -368,7 +515,6 @@
         assert fq._triggered == 1
 
     def test_finalizer_trigger_calls_too_much(self):
-        from rpython.rtyper.lltypesystem import lltype, rffi
         external_func = rffi.llexternal("foo", [], lltype.Void)
         # ^^^ with release_gil=True
         class X(object):
diff --git a/rpython/rtyper/lltypesystem/lltype.py 
b/rpython/rtyper/lltypesystem/lltype.py
--- a/rpython/rtyper/lltypesystem/lltype.py
+++ b/rpython/rtyper/lltypesystem/lltype.py
@@ -2174,7 +2174,8 @@
 
 
 def malloc(T, n=None, flavor='gc', immortal=False, zero=False,
-           track_allocation=True, add_memory_pressure=False):
+           track_allocation=True, add_memory_pressure=False,
+           nonmovable=False):
     assert flavor in ('gc', 'raw')
     if zero or immortal:
         initialization = 'example'
@@ -2200,7 +2201,8 @@
 
 @analyzer_for(malloc)
 def ann_malloc(s_T, s_n=None, s_flavor=None, s_zero=None,
-               s_track_allocation=None, s_add_memory_pressure=None):
+               s_track_allocation=None, s_add_memory_pressure=None,
+               s_nonmovable=None):
     assert (s_n is None or s_n.knowntype == int
             or issubclass(s_n.knowntype, base_int))
     assert s_T.is_constant()
@@ -2218,6 +2220,7 @@
         assert s_track_allocation is None or s_track_allocation.is_constant()
         assert (s_add_memory_pressure is None or
                 s_add_memory_pressure.is_constant())
+        assert s_nonmovable is None or s_nonmovable.is_constant()
         # not sure how to call malloc() for the example 'p' in the
         # presence of s_extraargs
         r = SomePtr(Ptr(s_T.const))
diff --git a/rpython/rtyper/lltypesystem/test/test_lltype.py 
b/rpython/rtyper/lltypesystem/test/test_lltype.py
--- a/rpython/rtyper/lltypesystem/test/test_lltype.py
+++ b/rpython/rtyper/lltypesystem/test/test_lltype.py
@@ -659,6 +659,7 @@
         a[3] = 30
         a[4] = 40
         b0 = direct_arrayitems(a)
+        assert typeOf(b0) == Ptr(FixedSizeArray(Signed, 1))
         b1 = direct_ptradd(b0, 1)
         b2 = direct_ptradd(b1, 1)
         b3 = direct_ptradd(b0, 3)
diff --git a/rpython/rtyper/rbuiltin.py b/rpython/rtyper/rbuiltin.py
--- a/rpython/rtyper/rbuiltin.py
+++ b/rpython/rtyper/rbuiltin.py
@@ -348,7 +348,7 @@
 
 @typer_for(lltype.malloc)
 def rtype_malloc(hop, i_flavor=None, i_zero=None, i_track_allocation=None,
-                 i_add_memory_pressure=None):
+                 i_add_memory_pressure=None, i_nonmovable=None):
     assert hop.args_s[0].is_constant()
     vlist = [hop.inputarg(lltype.Void, arg=0)]
     opname = 'malloc'
@@ -357,8 +357,10 @@
         (i_flavor, lltype.Void),
         (i_zero, None),
         (i_track_allocation, None),
-        (i_add_memory_pressure, None))
-    (v_flavor, v_zero, v_track_allocation, v_add_memory_pressure) = kwds_v
+        (i_add_memory_pressure, None),
+        (i_nonmovable, None))
+    (v_flavor, v_zero, v_track_allocation,
+     v_add_memory_pressure, v_nonmovable) = kwds_v
     flags = {'flavor': 'gc'}
     if v_flavor is not None:
         flags['flavor'] = v_flavor.value
@@ -368,6 +370,8 @@
         flags['track_allocation'] = v_track_allocation.value
     if i_add_memory_pressure is not None:
         flags['add_memory_pressure'] = v_add_memory_pressure.value
+    if i_nonmovable is not None:
+        flags['nonmovable'] = v_nonmovable
     vlist.append(hop.inputconst(lltype.Void, flags))
 
     assert 1 <= hop.nb_args <= 2
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to