PageRenderTime 151ms CodeModel.GetById 70ms app.highlight 55ms RepoModel.GetById 16ms app.codeStats 0ms

/pypy/jit/backend/llsupport/test/test_gc.py

https://bitbucket.org/pypy/pypy/
Python | 462 lines | 392 code | 39 blank | 31 comment | 32 complexity | a43b74401a212d402a62c15f171f3b2c MD5 | raw file
  1import random
  2from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr
  3from pypy.rpython.lltypesystem.lloperation import llop
  4from pypy.rpython.annlowlevel import llhelper
  5from pypy.jit.backend.llsupport.descr import *
  6from pypy.jit.backend.llsupport.gc import *
  7from pypy.jit.backend.llsupport import symbolic
  8from pypy.jit.metainterp.gc import get_description
  9from pypy.jit.metainterp.history import BoxPtr, BoxInt, ConstPtr
 10from pypy.jit.metainterp.resoperation import get_deep_immutable_oplist
 11from pypy.jit.tool.oparser import parse
 12from pypy.rpython.lltypesystem.rclass import OBJECT, OBJECT_VTABLE
 13from pypy.jit.metainterp.optimizeopt.util import equaloplists
 14
 15def test_boehm():
 16    gc_ll_descr = GcLLDescr_boehm(None, None, None)
 17    #
 18    record = []
 19    prev_malloc_fn_ptr = gc_ll_descr.malloc_fn_ptr
 20    def my_malloc_fn_ptr(size):
 21        p = prev_malloc_fn_ptr(size)
 22        record.append((size, p))
 23        return p
 24    gc_ll_descr.malloc_fn_ptr = my_malloc_fn_ptr
 25    #
 26    # ---------- gc_malloc ----------
 27    S = lltype.GcStruct('S', ('x', lltype.Signed))
 28    sizedescr = get_size_descr(gc_ll_descr, S)
 29    p = gc_ll_descr.gc_malloc(sizedescr)
 30    assert record == [(sizedescr.size, p)]
 31    del record[:]
 32    # ---------- gc_malloc_array ----------
 33    A = lltype.GcArray(lltype.Signed)
 34    arraydescr = get_array_descr(gc_ll_descr, A)
 35    p = gc_ll_descr.gc_malloc_array(arraydescr, 10)
 36    assert record == [(arraydescr.basesize +
 37                       10 * arraydescr.itemsize, p)]
 38    del record[:]
 39    # ---------- gc_malloc_str ----------
 40    p = gc_ll_descr.gc_malloc_str(10)
 41    basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR, False)
 42    assert record == [(basesize + 10 * itemsize, p)]
 43    del record[:]
 44    # ---------- gc_malloc_unicode ----------
 45    p = gc_ll_descr.gc_malloc_unicode(10)
 46    basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.UNICODE,
 47                                                              False)
 48    assert record == [(basesize + 10 * itemsize, p)]
 49    del record[:]
 50
 51# ____________________________________________________________
 52
 53
 54class TestGcRootMapAsmGcc:
 55
 56    def test_make_shapes(self):
 57        def frame_pos(n):
 58            return -4*(4+n)
 59        gcrootmap = GcRootMap_asmgcc()
 60        num1 = frame_pos(-5)
 61        num1a = num1|2
 62        num2 = frame_pos(55)
 63        num2a = ((-num2|3) >> 7) | 128
 64        num2b = (-num2|3) & 127
 65        shape = gcrootmap.get_basic_shape()
 66        gcrootmap.add_frame_offset(shape, num1)
 67        gcrootmap.add_frame_offset(shape, num2)
 68        assert shape == map(chr, [6, 7, 11, 15, 2, 0, num1a, num2b, num2a])
 69        gcrootmap.add_callee_save_reg(shape, 1)
 70        assert shape == map(chr, [6, 7, 11, 15, 2, 0, num1a, num2b, num2a,
 71                                  4])
 72        gcrootmap.add_callee_save_reg(shape, 2)
 73        assert shape == map(chr, [6, 7, 11, 15, 2, 0, num1a, num2b, num2a,
 74                                  4, 8])
 75        gcrootmap.add_callee_save_reg(shape, 3)
 76        assert shape == map(chr, [6, 7, 11, 15, 2, 0, num1a, num2b, num2a,
 77                                  4, 8, 12])
 78        gcrootmap.add_callee_save_reg(shape, 4)
 79        assert shape == map(chr, [6, 7, 11, 15, 2, 0, num1a, num2b, num2a,
 80                                  4, 8, 12, 16])
 81
 82    def test_compress_callshape(self):
 83        class FakeDataBlockWrapper:
 84            def malloc_aligned(self, size, alignment):
 85                assert alignment == 1    # here
 86                assert size == 4
 87                return rffi.cast(lltype.Signed, p)
 88        datablockwrapper = FakeDataBlockWrapper()
 89        p = lltype.malloc(rffi.CArray(lltype.Char), 4, immortal=True)
 90        gcrootmap = GcRootMap_asmgcc()
 91        shape = ['a', 'b', 'c', 'd']
 92        gcrootmap.compress_callshape(shape, datablockwrapper)
 93        assert p[0] == 'd'
 94        assert p[1] == 'c'
 95        assert p[2] == 'b'
 96        assert p[3] == 'a'
 97
 98    def test_put_basic(self):
 99        gcrootmap = GcRootMap_asmgcc()
100        retaddr = 1234567890
101        shapeaddr = 51627384
102        gcrootmap.put(retaddr, shapeaddr)
103        assert gcrootmap._gcmap[0] == retaddr
104        assert gcrootmap._gcmap[1] == shapeaddr
105        p = rffi.cast(rffi.LONGP, gcrootmap.gcmapstart())
106        assert p[0] == retaddr
107        assert (gcrootmap.gcmapend() ==
108                gcrootmap.gcmapstart() + rffi.sizeof(lltype.Signed) * 2)
109
110    def test_put_resize(self):
111        # the same as before, but enough times to trigger a few resizes
112        gcrootmap = GcRootMap_asmgcc()
113        for i in range(700):
114            shapeaddr = i * 100 + 1
115            retaddr = 123456789 + i
116            gcrootmap.put(retaddr, shapeaddr)
117        for i in range(700):
118            assert gcrootmap._gcmap[i*2+0] == 123456789 + i
119            assert gcrootmap._gcmap[i*2+1] == i * 100 + 1
120
121    def test_remove_nulls(self):
122        expected = []
123        def check():
124            assert gcrootmap._gcmap_curlength == len(expected) * 2
125            for i, (a, b) in enumerate(expected):
126                assert gcrootmap._gcmap[i*2] == a
127                assert gcrootmap._gcmap[i*2+1] == b
128        #
129        gcrootmap = GcRootMap_asmgcc()
130        for i in range(700):
131            shapeaddr = i * 100       # 0 if i == 0
132            retaddr = 123456789 + i
133            gcrootmap.put(retaddr, shapeaddr)
134            if shapeaddr != 0:
135                expected.append((retaddr, shapeaddr))
136        # at the first resize, the 0 should be removed
137        check()
138        for repeat in range(10):
139            # now clear up half the entries
140            assert len(expected) == 699
141            for i in range(0, len(expected), 2):
142                gcrootmap._gcmap[i*2+1] = 0
143                gcrootmap._gcmap_deadentries += 1
144            expected = expected[1::2]
145            assert gcrootmap._gcmap_deadentries*6 > gcrootmap._gcmap_maxlength
146            # check that we can again insert 350 entries without a resize
147            oldgcmap = gcrootmap._gcmap
148            for i in range(0, 699, 2):
149                gcrootmap.put(515151 + i + repeat, 626262 + i)
150                expected.append((515151 + i + repeat, 626262 + i))
151            assert gcrootmap._gcmap == oldgcmap
152            check()
153
154    def test_freeing_block(self):
155        from pypy.jit.backend.llsupport import gc
156        class Asmgcroot:
157            arrayitemsize = 2 * llmemory.sizeof(llmemory.Address)
158            sort_count = 0
159            def sort_gcmap(self, gcmapstart, gcmapend):
160                self.sort_count += 1
161            def binary_search(self, gcmapstart, gcmapend, startaddr):
162                i = 0
163                while (i < gcrootmap._gcmap_curlength//2 and
164                       gcrootmap._gcmap[i*2] < startaddr):
165                    i += 1
166                if i > 0:
167                    i -= 1
168                assert 0 <= i < gcrootmap._gcmap_curlength//2
169                p = rffi.cast(rffi.CArrayPtr(llmemory.Address), gcmapstart)
170                p = rffi.ptradd(p, 2*i)
171                return llmemory.cast_ptr_to_adr(p)
172        saved = gc.asmgcroot
173        try:
174            gc.asmgcroot = Asmgcroot()
175            #
176            gcrootmap = GcRootMap_asmgcc()
177            gcrootmap._gcmap = lltype.malloc(gcrootmap.GCMAP_ARRAY,
178                                             1400, flavor='raw',
179                                             immortal=True)
180            for i in range(700):
181                gcrootmap._gcmap[i*2] = 1200000 + i
182                gcrootmap._gcmap[i*2+1] = i * 100 + 1
183            assert gcrootmap._gcmap_deadentries == 0
184            assert gc.asmgcroot.sort_count == 0
185            gcrootmap._gcmap_maxlength = 1400
186            gcrootmap._gcmap_curlength = 1400
187            gcrootmap._gcmap_sorted = False
188            #
189            gcrootmap.freeing_block(1200000 - 100, 1200000)
190            assert gcrootmap._gcmap_deadentries == 0
191            assert gc.asmgcroot.sort_count == 1
192            #
193            gcrootmap.freeing_block(1200000 + 100, 1200000 + 200)
194            assert gcrootmap._gcmap_deadentries == 100
195            assert gc.asmgcroot.sort_count == 1
196            for i in range(700):
197                if 100 <= i < 200:
198                    expected = 0
199                else:
200                    expected = i * 100 + 1
201                assert gcrootmap._gcmap[i*2] == 1200000 + i
202                assert gcrootmap._gcmap[i*2+1] == expected
203            #
204            gcrootmap.freeing_block(1200000 + 650, 1200000 + 750)
205            assert gcrootmap._gcmap_deadentries == 150
206            assert gc.asmgcroot.sort_count == 1
207            for i in range(700):
208                if 100 <= i < 200 or 650 <= i:
209                    expected = 0
210                else:
211                    expected = i * 100 + 1
212                assert gcrootmap._gcmap[i*2] == 1200000 + i
213                assert gcrootmap._gcmap[i*2+1] == expected
214        #
215        finally:
216            gc.asmgcroot = saved
217
218
219class TestGcRootMapShadowStack:
220    class FakeGcDescr:
221        force_index_ofs = 92
222
223    def test_make_shapes(self):
224        gcrootmap = GcRootMap_shadowstack(self.FakeGcDescr())
225        shape = gcrootmap.get_basic_shape()
226        gcrootmap.add_frame_offset(shape, 16)
227        gcrootmap.add_frame_offset(shape, -24)
228        assert shape == [16, -24]
229
230    def test_compress_callshape(self):
231        class FakeDataBlockWrapper:
232            def malloc_aligned(self, size, alignment):
233                assert alignment == 4    # even on 64-bits
234                assert size == 12        # 4*3, even on 64-bits
235                return rffi.cast(lltype.Signed, p)
236        datablockwrapper = FakeDataBlockWrapper()
237        p = lltype.malloc(rffi.CArray(rffi.INT), 3, immortal=True)
238        gcrootmap = GcRootMap_shadowstack(self.FakeGcDescr())
239        shape = [16, -24]
240        gcrootmap.compress_callshape(shape, datablockwrapper)
241        assert rffi.cast(lltype.Signed, p[0]) == 16
242        assert rffi.cast(lltype.Signed, p[1]) == -24
243        assert rffi.cast(lltype.Signed, p[2]) == 0
244
245
246class FakeLLOp(object):
247    def __init__(self):
248        self.record = []
249
250    def _malloc(self, type_id, size):
251        tid = llop.combine_ushort(lltype.Signed, type_id, 0)
252        x = llmemory.raw_malloc(self.gcheaderbuilder.size_gc_header + size)
253        x += self.gcheaderbuilder.size_gc_header
254        return x, tid
255
256    def do_malloc_fixedsize_clear(self, RESTYPE, type_id, size,
257                                  has_finalizer, has_light_finalizer,
258                                  contains_weakptr):
259        assert not contains_weakptr
260        assert not has_finalizer
261        assert not has_light_finalizer
262        p, tid = self._malloc(type_id, size)
263        p = llmemory.cast_adr_to_ptr(p, RESTYPE)
264        self.record.append(("fixedsize", repr(size), tid, p))
265        return p
266
267    def do_malloc_varsize_clear(self, RESTYPE, type_id, length, size,
268                                itemsize, offset_to_length):
269        p, tid = self._malloc(type_id, size + itemsize * length)
270        (p + offset_to_length).signed[0] = length
271        p = llmemory.cast_adr_to_ptr(p, RESTYPE)
272        self.record.append(("varsize", tid, length,
273                            repr(size), repr(itemsize),
274                            repr(offset_to_length), p))
275        return p
276
277    def _write_barrier_failing_case(self, adr_struct, adr_newptr):
278        self.record.append(('barrier', adr_struct, adr_newptr))
279
280    def get_write_barrier_failing_case(self, FPTRTYPE):
281        return llhelper(FPTRTYPE, self._write_barrier_failing_case)
282
283    _have_wb_from_array = False
284
285    def _write_barrier_from_array_failing_case(self, adr_struct, v_index):
286        self.record.append(('barrier_from_array', adr_struct, v_index))
287
288    def get_write_barrier_from_array_failing_case(self, FPTRTYPE):
289        if self._have_wb_from_array:
290            return llhelper(FPTRTYPE,
291                            self._write_barrier_from_array_failing_case)
292        else:
293            return lltype.nullptr(FPTRTYPE.TO)
294
295
296class TestFramework(object):
297    gc = 'hybrid'
298
299    def setup_method(self, meth):
300        class config_(object):
301            class translation(object):
302                gc = self.gc
303                gcrootfinder = 'asmgcc'
304                gctransformer = 'framework'
305                gcremovetypeptr = False
306        class FakeTranslator(object):
307            config = config_
308        class FakeCPU(object):
309            def cast_adr_to_int(self, adr):
310                if not adr:
311                    return 0
312                try:
313                    ptr = llmemory.cast_adr_to_ptr(adr, gc_ll_descr.WB_FUNCPTR)
314                    assert ptr._obj._callable == \
315                           llop1._write_barrier_failing_case
316                    return 42
317                except lltype.InvalidCast:
318                    ptr = llmemory.cast_adr_to_ptr(
319                        adr, gc_ll_descr.WB_ARRAY_FUNCPTR)
320                    assert ptr._obj._callable == \
321                           llop1._write_barrier_from_array_failing_case
322                    return 43
323
324        gcdescr = get_description(config_)
325        translator = FakeTranslator()
326        llop1 = FakeLLOp()
327        gc_ll_descr = GcLLDescr_framework(gcdescr, FakeTranslator(), None,
328                                          llop1)
329        gc_ll_descr.initialize()
330        llop1.gcheaderbuilder = gc_ll_descr.gcheaderbuilder
331        self.llop1 = llop1
332        self.gc_ll_descr = gc_ll_descr
333        self.fake_cpu = FakeCPU()
334
335##    def test_args_for_new(self):
336##        S = lltype.GcStruct('S', ('x', lltype.Signed))
337##        sizedescr = get_size_descr(self.gc_ll_descr, S)
338##        args = self.gc_ll_descr.args_for_new(sizedescr)
339##        for x in args:
340##            assert lltype.typeOf(x) == lltype.Signed
341##        A = lltype.GcArray(lltype.Signed)
342##        arraydescr = get_array_descr(self.gc_ll_descr, A)
343##        args = self.gc_ll_descr.args_for_new(sizedescr)
344##        for x in args:
345##            assert lltype.typeOf(x) == lltype.Signed
346
347    def test_gc_malloc(self):
348        S = lltype.GcStruct('S', ('x', lltype.Signed))
349        sizedescr = get_size_descr(self.gc_ll_descr, S)
350        p = self.gc_ll_descr.gc_malloc(sizedescr)
351        assert lltype.typeOf(p) == llmemory.GCREF
352        assert self.llop1.record == [("fixedsize", repr(sizedescr.size),
353                                      sizedescr.tid, p)]
354
355    def test_gc_malloc_array(self):
356        A = lltype.GcArray(lltype.Signed)
357        arraydescr = get_array_descr(self.gc_ll_descr, A)
358        p = self.gc_ll_descr.gc_malloc_array(arraydescr, 10)
359        assert self.llop1.record == [("varsize", arraydescr.tid, 10,
360                                      repr(arraydescr.basesize),
361                                      repr(arraydescr.itemsize),
362                                      repr(arraydescr.lendescr.offset),
363                                      p)]
364
365    def test_gc_malloc_str(self):
366        p = self.gc_ll_descr.gc_malloc_str(10)
367        type_id = self.gc_ll_descr.layoutbuilder.get_type_id(rstr.STR)
368        tid = llop.combine_ushort(lltype.Signed, type_id, 0)
369        basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR,
370                                                                  True)
371        assert self.llop1.record == [("varsize", tid, 10,
372                                      repr(basesize), repr(itemsize),
373                                      repr(ofs_length), p)]
374
375    def test_gc_malloc_unicode(self):
376        p = self.gc_ll_descr.gc_malloc_unicode(10)
377        type_id = self.gc_ll_descr.layoutbuilder.get_type_id(rstr.UNICODE)
378        tid = llop.combine_ushort(lltype.Signed, type_id, 0)
379        basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.UNICODE,
380                                                                  True)
381        assert self.llop1.record == [("varsize", tid, 10,
382                                      repr(basesize), repr(itemsize),
383                                      repr(ofs_length), p)]
384
385    def test_do_write_barrier(self):
386        gc_ll_descr = self.gc_ll_descr
387        R = lltype.GcStruct('R')
388        S = lltype.GcStruct('S', ('r', lltype.Ptr(R)))
389        s = lltype.malloc(S)
390        r = lltype.malloc(R)
391        s_hdr = gc_ll_descr.gcheaderbuilder.new_header(s)
392        s_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, s)
393        r_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, r)
394        s_adr = llmemory.cast_ptr_to_adr(s)
395        r_adr = llmemory.cast_ptr_to_adr(r)
396        #
397        s_hdr.tid &= ~gc_ll_descr.GCClass.JIT_WB_IF_FLAG
398        gc_ll_descr.do_write_barrier(s_gcref, r_gcref)
399        assert self.llop1.record == []    # not called
400        #
401        s_hdr.tid |= gc_ll_descr.GCClass.JIT_WB_IF_FLAG
402        gc_ll_descr.do_write_barrier(s_gcref, r_gcref)
403        assert self.llop1.record == [('barrier', s_adr, r_adr)]
404
405    def test_gen_write_barrier(self):
406        gc_ll_descr = self.gc_ll_descr
407        llop1 = self.llop1
408        #
409        rewriter = GcRewriterAssembler(gc_ll_descr, None)
410        newops = rewriter.newops
411        v_base = BoxPtr()
412        v_value = BoxPtr()
413        rewriter.gen_write_barrier(v_base, v_value)
414        assert llop1.record == []
415        assert len(newops) == 1
416        assert newops[0].getopnum() == rop.COND_CALL_GC_WB
417        assert newops[0].getarg(0) == v_base
418        assert newops[0].getarg(1) == v_value
419        assert newops[0].result is None
420        wbdescr = newops[0].getdescr()
421        assert isinstance(wbdescr.jit_wb_if_flag, int)
422        assert isinstance(wbdescr.jit_wb_if_flag_byteofs, int)
423        assert isinstance(wbdescr.jit_wb_if_flag_singlebyte, int)
424
425    def test_get_rid_of_debug_merge_point(self):
426        operations = [
427            ResOperation(rop.DEBUG_MERGE_POINT, ['dummy', 2], None),
428            ]
429        gc_ll_descr = self.gc_ll_descr
430        operations = gc_ll_descr.rewrite_assembler(None, operations, [])
431        assert len(operations) == 0
432
433    def test_record_constptrs(self):
434        class MyFakeCPU(object):
435            def cast_adr_to_int(self, adr):
436                assert adr == "some fake address"
437                return 43
438        class MyFakeGCRefList(object):
439            def get_address_of_gcref(self, s_gcref1):
440                assert s_gcref1 == s_gcref
441                return "some fake address"
442        S = lltype.GcStruct('S')
443        s = lltype.malloc(S)
444        s_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, s)
445        v_random_box = BoxPtr()
446        v_result = BoxInt()
447        operations = [
448            ResOperation(rop.PTR_EQ, [v_random_box, ConstPtr(s_gcref)],
449                         v_result),
450            ]
451        gc_ll_descr = self.gc_ll_descr
452        gc_ll_descr.gcrefs = MyFakeGCRefList()
453        gcrefs = []
454        operations = get_deep_immutable_oplist(operations)
455        operations2 = gc_ll_descr.rewrite_assembler(MyFakeCPU(), operations,
456                                                   gcrefs)
457        assert operations2 == operations
458        assert gcrefs == [s_gcref]
459
460
461class TestFrameworkMiniMark(TestFramework):
462    gc = 'minimark'