def test_find_clean_setarrayitems_2(): S = lltype.GcStruct('S') A = lltype.GcArray(lltype.Ptr(S)) def f(): l = lltype.malloc(A, 3) l[0] = lltype.malloc(S) l[1] = lltype.malloc(S) l[2] = lltype.malloc(S) x = l[1] l[2] = lltype.malloc(S) # <- this can possibly collect l[0] = x return len(l) t = rtype(f, []) etrafo = ExceptionTransformer(t) graph = etrafo.transform_completely() collect_analyzer = CollectAnalyzer(t) clean_setarrayitems = find_clean_setarrayitems(collect_analyzer, t.graphs[0]) assert len(clean_setarrayitems) == 0
def test_along_link(): S1 = lltype.GcStruct('S1', ('x', lltype.Signed), hints={'immutable': True}) s1 = lltype.malloc(S1) s1.x = 123 s2 = lltype.malloc(S1) s2.x = 60 def fn(x): if x: x = s1.x else: x = s2.x return x+1 graph, t = get_graph(fn, [int]) assert summary(graph) == {'int_is_true': 1, 'getfield': 2, 'int_add': 1} constant_fold_graph(graph) assert summary(graph) == {'int_is_true': 1} check_graph(graph, [-1], 124, t) check_graph(graph, [0], 61, t)
def test_keepalive_const_fieldptr(): S1 = lltype.GcStruct('S1', ('x', lltype.Signed)) s1 = lltype.malloc(S1) s1.x = 1234 def fn(): p1 = lltype.direct_fieldptr(s1, 'x') return p1[0] graph, t = get_graph(fn, []) assert summary(graph) == {'direct_fieldptr': 1, 'getarrayitem': 1} constant_fold_graph(graph) # kill all references to 's1' s1 = fn = None del graph.func import gc gc.collect() assert summary(graph) == {'getarrayitem': 1} check_graph(graph, [], 1234, t)
def get_shadowstackref(root_walker, gctransformer): if hasattr(gctransformer, '_SHADOWSTACKREF'): return gctransformer._SHADOWSTACKREF SHADOWSTACKREFPTR = lltype.Ptr(lltype.GcForwardReference()) SHADOWSTACKREF = lltype.GcStruct('ShadowStackRef', ('base', llmemory.Address), ('top', llmemory.Address), rtti=True) SHADOWSTACKREFPTR.TO.become(SHADOWSTACKREF) def customtrace(gc, obj, callback, arg1, arg2): obj = llmemory.cast_adr_to_ptr(obj, SHADOWSTACKREFPTR) walk_stack_root(gc._trace_callback, callback, arg1, arg2, obj.base, obj.top, is_minor=False) # xxx optimize? gc = gctransformer.gcdata.gc assert not hasattr(gc, 'custom_trace_dispatcher') # ^^^ create_custom_trace_funcs() must not run before this gctransformer.translator.rtyper.custom_trace_funcs.append( (SHADOWSTACKREF, customtrace)) def shadowstack_destructor(shadowstackref): base = shadowstackref.base shadowstackref.base = llmemory.NULL shadowstackref.top = llmemory.NULL llmemory.raw_free(base) destrptr = gctransformer.annotate_helper(shadowstack_destructor, [SHADOWSTACKREFPTR], lltype.Void) lltype.attachRuntimeTypeInfo(SHADOWSTACKREF, destrptr=destrptr) gctransformer._SHADOWSTACKREF = SHADOWSTACKREF return SHADOWSTACKREF
def test_keepalive_hard_case(self): from rpython.rtyper.lltypesystem import lltype Y = lltype.Struct('y', ('n', lltype.Signed)) X = lltype.GcStruct('x', ('y', Y)) def g(x): if x: return 3 else: return 4 def f(): x = lltype.malloc(X) x.y.n = 2 y = x.y z1 = g(y.n) z = y.n return z + z1 eval_func = self.check_inline(g, f, []) res = eval_func([]) assert res == 5
def test_llinterp_raw_malloc_struct(): T = lltype.GcStruct('T', ('z', lltype.Signed)) S = lltype.Struct('S', ('x', lltype.Signed), ('y', lltype.Ptr(T))) size = sizeof(S) def test_read_uninit(): adr = raw_malloc(size) s = cast_adr_to_ptr(adr, lltype.Ptr(S)) return s.x py.test.raises(lltype.UninitializedMemoryAccess, "interpret(test_read_uninit, [])") def test_read_init(): adr = raw_malloc(size) raw_memclear(adr, size) s = cast_adr_to_ptr(adr, lltype.Ptr(S)) return s.x res = interpret(test_read_init, []) assert res == 0
def define_no_clean_setarrayitems(cls): # The optimization find_clean_setarrayitems() in # gctransformer/framework.py does not work with card marking. # Check that it is turned off. S = lltype.GcStruct('S', ('x', lltype.Signed)) A = lltype.GcArray(lltype.Ptr(S)) def sub(lst): lst[15] = lltype.malloc(S) # 'lst' is set the single mark "12-15" lst[15].x = 123 lst[0] = lst[15] # that would be a "clean_setarrayitem" def f(): lst = lltype.malloc(A, 16) # 16 > 10 rgc.collect() sub(lst) null = lltype.nullptr(S) lst[15] = null # clear, so that A() is only visible via lst[0] rgc.collect() # -> crash return lst[0].x return f
def test_needs_keepalive(self): check_debug_build() from rpython.rtyper.lltypesystem import lltype X = lltype.GcStruct("X", ('y', lltype.Struct("Y", ('z', lltype.Signed)))) def can_raise(n): if n: raise Exception else: return 1 def foo(n): x = lltype.malloc(X) y = x.y y.z = 42 r = can_raise(n) return r + y.z f = self.compile(foo, [int]) res = f(0) assert res == 43
def test_adt_method(self): def ll_callme(n): return n ll_callme = lltype.staticAdtMethod(ll_callme) S = lltype.GcStruct('S', ('x', lltype.Signed), adtmeths = {'yep': True, 'callme': ll_callme}) def g(p, x, y, z): p.x = x if p.yep: z *= p.callme(y) return z def f(x, y, z): p = lltype.malloc(S) return g(p, x, y, z) t, wa = self.translate(f, [int, int, int]) fgraph = graphof(t, f) assert fgraph.startblock.operations[-1].opname == 'direct_call' result = wa.analyze(fgraph.startblock.operations[-1]) assert list(result) == [("struct", lltype.Ptr(S), "x")]
def define_gc_heap_stats(cls): S = lltype.GcStruct('S', ('x', lltype.Signed)) l1 = [] l2 = [] l3 = [] l4 = [] def f(): for i in range(10): s = lltype.malloc(S) l1.append(s) l2.append(s) if i < 3: l3.append(s) l4.append(s) # We cheat here and only read the table which we later on # process ourselves, otherwise this test takes ages llop.gc__collect(lltype.Void) tb = rgc._heap_stats() a = 0 nr = 0 b = 0 c = 0 d = 0 e = 0 for i in range(len(tb)): if tb[i].count == 10: a += 1 nr = i if tb[i].count > 50: d += 1 for i in range(len(tb)): if tb[i].count == 4: b += 1 c += tb[i].links[nr] e += tb[i].size return d * 1000 + c * 100 + b * 10 + a return f
def define_do_malloc_operations_in_call(cls): P = lltype.GcStruct('P', ('x', lltype.Signed)) def g(): llop.do_malloc_fixedsize(llmemory.GCREF) # placeholder def f(): q = lltype.malloc(P) q.x = 1 i = 0 while i < 40: g() i += q.x return 0 def fix_graph_of_g(translator): from rpython.translator.translator import graphof from rpython.flowspace.model import Constant from rpython.rtyper.lltypesystem import rffi layoutbuilder = cls.ensure_layoutbuilder(translator) type_id = layoutbuilder.get_type_id(P) # # now fix the do_malloc_fixedsize in the graph of g graph = graphof(translator, g) for op in graph.startblock.operations: if op.opname == 'do_malloc_fixedsize': op.args = [ Constant(type_id, llgroup.HALFWORD), Constant(llmemory.sizeof(P), lltype.Signed), Constant(False, lltype.Bool), # has_finalizer Constant(False, lltype.Bool), # is_finalizer_light Constant(False, lltype.Bool) ] # contains_weakptr break else: assert 0, "oups, not found" return f, None, fix_graph_of_g
def test_NotVirtualStateInfo_generalization(self): def isgeneral(value1, value2): info1 = NotVirtualStateInfo(value1) info1.position = 0 info2 = NotVirtualStateInfo(value2) info2.position = 0 return info1.generalization_of(info2, {}, {}) assert isgeneral(OptValue(BoxInt()), OptValue(ConstInt(7))) assert not isgeneral(OptValue(ConstInt(7)), OptValue(BoxInt())) ptr = OptValue(BoxPtr()) nonnull = OptValue(BoxPtr()) nonnull.make_nonnull(0) knownclass = OptValue(BoxPtr()) knownclass.make_constant_class(ConstPtr(self.someptr1), 0) const = OptValue(BoxPtr) const.make_constant_class(ConstPtr(self.someptr1), 0) const.make_constant(ConstPtr(self.someptr1)) inorder = [ptr, nonnull, knownclass, const] for i in range(len(inorder)): for j in range(i, len(inorder)): assert isgeneral(inorder[i], inorder[j]) if i != j: assert not isgeneral(inorder[j], inorder[i]) value1 = OptValue(BoxInt()) value2 = OptValue(BoxInt()) value2.intbound.make_lt(IntBound(10, 10)) assert isgeneral(value1, value2) assert not isgeneral(value2, value1) assert isgeneral(OptValue(ConstInt(7)), OptValue(ConstInt(7))) S = lltype.GcStruct('S') foo = lltype.malloc(S) fooref = lltype.cast_opaque_ptr(llmemory.GCREF, foo) assert isgeneral(OptValue(ConstPtr(fooref)), OptValue(ConstPtr(fooref)))
def test_fold_exitswitch(): S1 = lltype.GcStruct('S1', ('x', lltype.Signed), hints={'immutable': True}) s1 = lltype.malloc(S1) s1.x = 123 s2 = lltype.malloc(S1) s2.x = 60 def fn(n): if s1.x: return n * 5 else: return n - 7 graph, t = get_graph(fn, [int]) assert summary(graph) == { 'getfield': 1, 'int_is_true': 1, 'int_mul': 1, 'int_sub': 1 } constant_fold_graph(graph) assert summary(graph) == {'int_mul': 1} check_graph(graph, [12], 60, t)
def define_nongc_static_root_minor_collect(cls): T1 = lltype.GcStruct("C", ('x', lltype.Signed)) T2 = lltype.Struct("C", ('p', lltype.Ptr(T1))) static = lltype.malloc(T2, immortal=True) def f(): t1 = lltype.malloc(T1) t1.x = 42 static.p = t1 x = 20 all = [None] * x i = 0 while i < x: # enough to cause a minor collect all[i] = [i] * i i += 1 i = static.p.x llop.gc__collect(lltype.Void) return static.p.x + i def cleanup(): static.p = lltype.nullptr(T1) return f, cleanup, None
def test_llhelper_stored_in_struct(self): from rpython.rtyper.annlowlevel import llhelper def f(x): return x + 3 FUNC_TP = lltype.Ptr(lltype.FuncType([lltype.Signed], lltype.Signed)) S = lltype.GcStruct('s', ('f', FUNC_TP)) class Glob(object): pass glob = Glob() def entry_point(argv): x = llhelper(FUNC_TP, f) s = lltype.malloc(S) s.f = x glob.s = s # escape return 0 self.compile(entry_point)
def __init__(self, rtyper): super(RuleRepr, self).__init__() self.ll_rule_cache = {} self.match_init_repr = rtyper.getrepr( rtyper.annotator.bookkeeper.immutablevalue(Match.__init__)) self.match_context_init_repr = rtyper.getrepr( rtyper.annotator.bookkeeper.immutablevalue( rsre_core.StrMatchContext.__init__)) self.match_context_repr = rtyper.getrepr( rtyper.annotator.bookkeeper.immutablevalue( rsre_core.match_context)) list_repr = FixedSizeListRepr( rtyper, rtyper.getrepr(model.SomeInteger(nonneg=True))) list_repr._setup_repr() self.lowleveltype = lltype.Ptr( lltype.GcStruct( "RULE", ("name", lltype.Ptr(STR)), ("code", list_repr.lowleveltype), ))
def test_nullity_with_guard(self): allops = [rop.INT_IS_TRUE] guards = [rop.GUARD_TRUE, rop.GUARD_FALSE] p = lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(lltype.GcStruct('x'))) nullptr = lltype.nullptr(llmemory.GCREF.TO) f = InputArgInt() for op in allops: for guard in guards: if op == rop.INT_IS_TRUE: bp = InputArgInt(1) n = InputArgInt(0) else: bp = InputArgRef(p) n = InputArgRef(nullptr) for b in (bp, n): i1 = ResOperation(rop.SAME_AS_I, [ConstInt(1)]) f = ResOperation(op, [b]) ops = [ i1, f, ResOperation(guard, [f], descr=BasicFailDescr()), ResOperation(rop.FINISH, [ConstInt(0)], descr=BasicFinalDescr()), ] ops[-2].setfailargs([i1]) looptoken = JitCellToken() self.cpu.compile_loop([b], ops, looptoken) deadframe = self.cpu.execute_token(looptoken, b.getint()) result = self.cpu.get_int_value(deadframe, 0) if guard == rop.GUARD_FALSE: assert result == execute(self.cpu, None, op, None, b) else: assert result != execute(self.cpu, None, op, None, b)
def test_memoryerror(): # in rev 30717 this test causes a segfault on some Linux, but usually # only after the test is run. It is caused by the following sequence # of events in lltype.malloc(S, n): there is an OP_MAX_VARSIZE macro # which figures out that the size asked for is too large and would # cause a wrap-around, so it sets a MemoryError; but execution continues # nevertheless and the next line is an OP_MALLOC instruction, which # because of the wrap-around allocates for 's' an amount of bytes which # falls precisely between 0 and offsetof(s, tail). It succeeds. Then # the length field of s.tail is initialized - this overwrites random # memory! And only then is the exception check performed, and the # MemoryError is noticed. A = lltype.Array(lltype.Signed) S = lltype.GcStruct('S', ('a', lltype.Signed), ('b', lltype.Signed), ('c', lltype.Signed), ('tail', A)) def g(n, tag): s = lltype.malloc(S, n) tag.a = 42 return s def testfn(n): tag = lltype.malloc(S, 0) try: s = g(n, tag) result = s.tail[n // 2] except MemoryError: result = 1000 return result + tag.a f1 = getcompiled(testfn, [int]) assert f1(10) == 42 assert f1(sys.maxint) == 1000 for i in range(20): assert f1(int((sys.maxint + 1) // 2 - i)) == 1000 assert f1(sys.maxint // 2 - 16384) == 1000 assert f1(sys.maxint // 2 + 16384) == 1000
def test_wrap(): def _is(box1, box2): return (box1.__class__ == box2.__class__ and box1.value == box2.value) p = lltype.malloc(lltype.GcStruct('S')) po = lltype.cast_opaque_ptr(llmemory.GCREF, p) assert _is(wrap(None, 42), BoxInt(42)) assert _is(wrap(None, 42.5), boxfloat(42.5)) assert _is(wrap(None, p), BoxPtr(po)) assert _is(wrap(None, 42, in_const_box=True), ConstInt(42)) assert _is(wrap(None, 42.5, in_const_box=True), constfloat(42.5)) assert _is(wrap(None, p, in_const_box=True), ConstPtr(po)) if longlong.supports_longlong: import sys from rpython.rlib.rarithmetic import r_longlong, r_ulonglong value = r_longlong(-sys.maxint*17) assert _is(wrap(None, value), BoxFloat(value)) assert _is(wrap(None, value, in_const_box=True), ConstFloat(value)) value_unsigned = r_ulonglong(-sys.maxint*17) assert _is(wrap(None, value_unsigned), BoxFloat(value)) sfval = r_singlefloat(42.5) ival = longlong.singlefloat2int(sfval) assert _is(wrap(None, sfval), BoxInt(ival)) assert _is(wrap(None, sfval, in_const_box=True), ConstInt(ival))
def test_boehm(): gc_ll_descr = gc.GcLLDescr_boehm(None, None, None) # record = [] prev_malloc_fn_ptr = gc_ll_descr.malloc_fn_ptr def my_malloc_fn_ptr(size): p = prev_malloc_fn_ptr(size) record.append((size, p)) return p gc_ll_descr.malloc_fn_ptr = my_malloc_fn_ptr # # ---------- gc_malloc ---------- S = lltype.GcStruct('S', ('x', lltype.Signed)) sizedescr = descr.get_size_descr(gc_ll_descr, S) p = gc_ll_descr.gc_malloc(sizedescr) assert record == [(sizedescr.size, p)] del record[:] # ---------- gc_malloc_array ---------- A = lltype.GcArray(lltype.Signed) arraydescr = descr.get_array_descr(gc_ll_descr, A) p = gc_ll_descr.gc_malloc_array(10, arraydescr) assert record == [(arraydescr.basesize + 10 * arraydescr.itemsize, p)] del record[:] # ---------- gc_malloc_str ---------- p = gc_ll_descr.gc_malloc_str(10) basesize, itemsize, ofs_length = symbolic.get_array_token(rstr.STR, False) assert record == [(basesize + 10 * itemsize, p)] del record[:] # ---------- gc_malloc_unicode ---------- p = gc_ll_descr.gc_malloc_unicode(10) basesize, itemsize, ofs_length = symbolic.get_array_token( rstr.UNICODE, False) assert record == [(basesize + 10 * itemsize, p)] del record[:]
def test_raw_malloc_gcstruct(): from rpython.memory import gcheader HDR = lltype.Struct('header', ('a', lltype.Signed)) builder = gcheader.GCHeaderBuilder(HDR) gchdr = builder.size_gc_header S = lltype.GcStruct('S', ('x', lltype.Signed)) def allocate(): adr = raw_malloc(gchdr + sizeof(S)) p = cast_adr_to_ptr(adr, lltype.Ptr(HDR)) p.a = -21 adr = cast_ptr_to_adr(p) sadr = adr + gchdr s = cast_adr_to_ptr(sadr, lltype.Ptr(S)) s.x = 123 assert (sadr + offsetof(S, 'x')).signed[0] == 123 (sadr + offsetof(S, 'x')).signed[0] = 125 assert s.x == 125 return s s = allocate() adr = cast_ptr_to_adr(s) - gchdr p = cast_adr_to_ptr(adr, lltype.Ptr(HDR)) assert p.a == -21
def test_assemble_cast_consts(): ssarepr = SSARepr("test") S = lltype.GcStruct('S') s = lltype.malloc(S) F = lltype.FuncType([], lltype.Signed) f = lltype.functionptr(F, 'f') ssarepr.insns = [ ('int_return', Constant('X', lltype.Char)), ('int_return', Constant(unichr(0x1234), lltype.UniChar)), ('int_return', Constant(f, lltype.Ptr(F))), ('ref_return', Constant(s, lltype.Ptr(S))), ] assembler = Assembler() jitcode = assembler.assemble(ssarepr) assert jitcode.code == ("\x00\x58" "\x01\xFF" "\x01\xFE" "\x02\xFF") assert assembler.insns == { 'int_return/c': 0, 'int_return/i': 1, 'ref_return/r': 2 } f_int = heaptracker.adr2int(llmemory.cast_ptr_to_adr(f)) assert jitcode.constants_i == [0x1234, f_int] s_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, s) assert jitcode.constants_r == [s_gcref]
def test_keepalive(self): S = lltype.GcStruct('S') def g(): return lltype.malloc(S) def f(x): p = g() q = g() keepalive_until_here(p) keepalive_until_here(q) return x self.encoding_test(f, [5], """ residual_call_r_r $<* fn g>, R[], <Descr> -> %r0 -live- residual_call_r_r $<* fn g>, R[], <Descr> -> %r1 -live- -live- %r0 -live- %r1 int_return %i0 """, transform=True) self.encoding_test(f, [5], """ residual_call_r_r $<* fn g>, R[], <Descr> -> %r0 -live- %i0, %r0 residual_call_r_r $<* fn g>, R[], <Descr> -> %r1 -live- %i0, %r0, %r1 -live- %i0, %r0, %r1 -live- %i0, %r1 int_return %i0 """, transform=True, liveness=True)
def define_compile_framework_7_interior(cls): # Array of structs containing pointers (test the write barrier # for setinteriorfield_gc) S = lltype.GcStruct('S', ('i', lltype.Signed)) A = lltype.GcArray(lltype.Struct('entry', ('x', lltype.Ptr(S)), ('y', lltype.Ptr(S)), ('z', lltype.Ptr(S)))) class Glob: a = lltype.nullptr(A) glob = Glob() # def make_s(i): s = lltype.malloc(S) s.i = i return s # @unroll_safe def f(n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s): a = glob.a if not a: a = glob.a = lltype.malloc(A, 10) i = 0 while i < 10: a[i].x = make_s(n + i * 100 + 1) a[i].y = make_s(n + i * 100 + 2) a[i].z = make_s(n + i * 100 + 3) i += 1 i = 0 while i < 10: check(a[i].x.i == n + i * 100 + 1) check(a[i].y.i == n + i * 100 + 2) check(a[i].z.i == n + i * 100 + 3) i += 1 n -= x.foo return n, x, x0, x1, x2, x3, x4, x5, x6, x7, l, s return None, f, None
class TestRegallocMoreRegisters(CustomBaseTestRegalloc): cpu = CustomBaseTestRegalloc.cpu targettoken = TargetToken() S = lltype.GcStruct('S', ('field', lltype.Char)) fielddescr = cpu.fielddescrof(S, 'field') A = lltype.GcArray(lltype.Char) I = lltype.GcArray(lltype.Signed) arraydescr = cpu.arraydescrof(A) arraydescr_i = cpu.arraydescrof(I) namespace = locals().copy() def test_int_is_true(self): ops = ''' [i0, i1, i2, i3, i4, i5, i6, i7] i10 = int_is_true(i0) i11 = int_is_true(i1) i12 = int_is_true(i2) i13 = int_is_true(i3) i14 = int_is_true(i4) i15 = int_is_true(i5) i16 = int_is_true(i6) i17 = int_is_true(i7) guard_true(i0) [i10, i11, i12, i13, i14, i15, i16, i17] ''' self.interpret(ops, [0, 42, 12, 0, 13, 0, 0, 3333]) assert self.getints(8) == [0, 1, 1, 0, 1, 0, 0, 1] def test_comparison_ops(self): ops = ''' [i0, i1, i2, i3, i4, i5, i6] i10 = int_lt(i0, i1) i11 = int_le(i2, i3) i12 = int_ge(i4, i5) i13 = int_eq(i5, i6) i14 = int_gt(i6, i2) i15 = int_ne(i2, i6) guard_true(i15) [i10, i11, i12, i13, i14, i15] ''' self.interpret(ops, [0, 1, 2, 3, 4, 5, 6]) assert self.getints(6) == [1, 1, 0, 0, 1, 1] def test_strsetitem(self): ops = ''' [p0, i] strsetitem(p0, 1, i) finish() ''' llstr = rstr.mallocstr(10) self.interpret(ops, [llstr, ord('a')]) assert llstr.chars[1] == 'a' def test_setfield_char(self): ops = ''' [p0, i] setfield_gc(p0, i, descr=fielddescr) finish() ''' s = lltype.malloc(self.S) self.interpret(ops, [s, ord('a')]) assert s.field == 'a' def test_setarrayitem_gc(self): ops = ''' [p0, i] setarrayitem_gc(p0, 1, i, descr=arraydescr) finish() ''' s = lltype.malloc(self.A, 3) self.interpret(ops, [s, ord('a')]) assert s[1] == 'a' def test_setarrayitem2_gc(self): ops = ''' [p0, i, i1] setarrayitem_gc(p0, i1, i, descr=arraydescr) finish() ''' s = lltype.malloc(self.A, 3) self.interpret(ops, [s, ord('a'), 1]) assert s[1] == 'a' def test_setarrayitem3_gc(self): ops = ''' [p0, i0, i1] setarrayitem_gc(p0, i1, i0, descr=arraydescr_i) finish() ''' s = lltype.malloc(self.I, 3) self.interpret(ops, [s, 1234567890, 1]) assert s[1] == 1234567890 def test_setarrayitem4_gc(self): ops = ''' [p0, i0] setarrayitem_gc(p0, 1, i0, descr=arraydescr_i) finish() ''' s = lltype.malloc(self.I, 3) self.interpret(ops, [s, 1234567890]) assert s[1] == 1234567890 def test_division_optimized(self): ops = ''' [i7, i6] label(i7, i6, descr=targettoken) i18 = int_floordiv(i7, i6) i19 = int_xor(i7, i6) i21 = int_lt(i19, 0) i22 = int_mod(i7, i6) i23 = int_is_true(i22) i24 = int_eq(i6, 4) guard_false(i24) [i18] jump(i18, i6, descr=targettoken) ''' self.interpret(ops, [10, 4]) assert self.getint(0) == 2
import py from rpython.rtyper.lltypesystem import lltype, llmemory from rpython.memory.gc.incminimark import IncrementalMiniMarkGC from rpython.memory.gc.test.test_direct import BaseDirectGCTest from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY from rpython.rlib.rawrefcount import REFCNT_FROM_PYPY_LIGHT PYOBJ_HDR = IncrementalMiniMarkGC.PYOBJ_HDR PYOBJ_HDR_PTR = IncrementalMiniMarkGC.PYOBJ_HDR_PTR S = lltype.GcForwardReference() S.become( lltype.GcStruct('S', ('x', lltype.Signed), ('prev', lltype.Ptr(S)), ('next', lltype.Ptr(S)))) class TestRawRefCount(BaseDirectGCTest): GCClass = IncrementalMiniMarkGC def _collect(self, major, expected_trigger=0): if major: self.gc.collect() else: self.gc._minor_collection() count1 = len(self.trigger) self.gc.rrc_invoke_callback() count2 = len(self.trigger) assert count2 - count1 == expected_trigger def _rawrefcount_pair(self, intval,
class SemiSpaceGC(MovingGCBase): _alloc_flavor_ = "raw" inline_simple_malloc = True inline_simple_malloc_varsize = True malloc_zero_filled = True first_unused_gcflag = first_gcflag << 6 gcflag_extra = GCFLAG_EXTRA HDR = lltype.Struct('header', ('tid', lltype.Signed)) # XXX or rffi.INT? typeid_is_in_field = 'tid' FORWARDSTUB = lltype.GcStruct('forwarding_stub', ('forw', llmemory.Address)) FORWARDSTUBPTR = lltype.Ptr(FORWARDSTUB) object_minimal_size = llmemory.sizeof(FORWARDSTUB) # the following values override the default arguments of __init__ when # translating to a real backend. TRANSLATION_PARAMS = {'space_size': 8 * 1024 * 1024} # XXX adjust def __init__(self, config, space_size=4096, max_space_size=sys.maxint // 2 + 1, **kwds): self.param_space_size = space_size self.param_max_space_size = max_space_size MovingGCBase.__init__(self, config, **kwds) def setup(self): #self.total_collection_time = 0.0 self.total_collection_count = 0 self.space_size = self.param_space_size self.max_space_size = self.param_max_space_size self.red_zone = 0 #self.program_start_time = time.time() self.tospace = llarena.arena_malloc(self.space_size, True) ll_assert(bool(self.tospace), "couldn't allocate tospace") self.top_of_space = self.tospace + self.space_size self.fromspace = llarena.arena_malloc(self.space_size, True) ll_assert(bool(self.fromspace), "couldn't allocate fromspace") self.free = self.tospace MovingGCBase.setup(self) self.objects_with_finalizers = self.AddressDeque() self.objects_with_light_finalizers = self.AddressStack() self.objects_with_weakrefs = self.AddressStack() def _teardown(self): debug_print("Teardown") llarena.arena_free(self.fromspace) llarena.arena_free(self.tospace) # This class only defines the malloc_{fixed,var}size_clear() methods # because the spaces are filled with zeroes in advance. def malloc_fixedsize_clear(self, typeid16, size, has_finalizer=False, is_finalizer_light=False, contains_weakptr=False): size_gc_header = self.gcheaderbuilder.size_gc_header totalsize = size_gc_header + size result = self.free if raw_malloc_usage(totalsize) > self.top_of_space - result: result = self.obtain_free_space(totalsize) llarena.arena_reserve(result, totalsize) self.init_gc_object(result, typeid16) self.free = result + totalsize #if is_finalizer_light: # self.objects_with_light_finalizers.append(result + size_gc_header) #else: if has_finalizer: from rpython.rtyper.lltypesystem import rffi self.objects_with_finalizers.append(result + size_gc_header) self.objects_with_finalizers.append(rffi.cast( llmemory.Address, -1)) if contains_weakptr: self.objects_with_weakrefs.append(result + size_gc_header) return llmemory.cast_adr_to_ptr(result + size_gc_header, llmemory.GCREF) def malloc_varsize_clear(self, typeid16, length, size, itemsize, offset_to_length): size_gc_header = self.gcheaderbuilder.size_gc_header nonvarsize = size_gc_header + size try: varsize = ovfcheck(itemsize * length) totalsize = ovfcheck(nonvarsize + varsize) except OverflowError: raise memoryError result = self.free if raw_malloc_usage(totalsize) > self.top_of_space - result: result = self.obtain_free_space(totalsize) llarena.arena_reserve(result, totalsize) self.init_gc_object(result, typeid16) (result + size_gc_header + offset_to_length).signed[0] = length self.free = result + llarena.round_up_for_allocation(totalsize) return llmemory.cast_adr_to_ptr(result + size_gc_header, llmemory.GCREF) def shrink_array(self, addr, smallerlength): size_gc_header = self.gcheaderbuilder.size_gc_header if self._is_in_the_space(addr - size_gc_header): typeid = self.get_type_id(addr) totalsmallersize = ( size_gc_header + self.fixed_size(typeid) + self.varsize_item_sizes(typeid) * smallerlength) llarena.arena_shrink_obj(addr - size_gc_header, totalsmallersize) # offset_to_length = self.varsize_offset_to_length(typeid) (addr + offset_to_length).signed[0] = smallerlength return True else: return False def register_finalizer(self, fq_index, gcobj): from rpython.rtyper.lltypesystem import rffi obj = llmemory.cast_ptr_to_adr(gcobj) fq_index = rffi.cast(llmemory.Address, fq_index) self.objects_with_finalizers.append(obj) self.objects_with_finalizers.append(fq_index) def obtain_free_space(self, needed): # a bit of tweaking to maximize the performance and minimize the # amount of code in an inlined version of malloc_fixedsize_clear() if not self.try_obtain_free_space(needed): raise memoryError return self.free obtain_free_space._dont_inline_ = True def try_obtain_free_space(self, needed): # XXX for bonus points do big objects differently needed = raw_malloc_usage(needed) if (self.red_zone >= 2 and self.space_size < self.max_space_size and self.double_space_size()): pass # collect was done during double_space_size() else: self.semispace_collect() missing = needed - (self.top_of_space - self.free) if missing <= 0: return True # success else: # first check if the object could possibly fit proposed_size = self.space_size while missing > 0: if proposed_size >= self.max_space_size: return False # no way missing -= proposed_size proposed_size *= 2 # For address space fragmentation reasons, we double the space # size possibly several times, moving the objects at each step, # instead of going directly for the final size. We assume that # it's a rare case anyway. while self.space_size < proposed_size: if not self.double_space_size(): return False ll_assert(needed <= self.top_of_space - self.free, "double_space_size() failed to do its job") return True def double_space_size(self): self.red_zone = 0 old_fromspace = self.fromspace newsize = self.space_size * 2 newspace = llarena.arena_malloc(newsize, True) if not newspace: return False # out of memory llarena.arena_free(old_fromspace) self.fromspace = newspace # now self.tospace contains the existing objects and # self.fromspace is the freshly allocated bigger space self.semispace_collect(size_changing=True) self.top_of_space = self.tospace + newsize # now self.tospace is the freshly allocated bigger space, # and self.fromspace is the old smaller space, now empty llarena.arena_free(self.fromspace) newspace = llarena.arena_malloc(newsize, True) if not newspace: # Complex failure case: we have in self.tospace a big chunk # of memory, and the two smaller original spaces are already gone. # Unsure if it's worth these efforts, but we can artificially # split self.tospace in two again... self.max_space_size = self.space_size # don't try to grow again, # because doing arena_free(self.fromspace) would crash self.fromspace = self.tospace + self.space_size self.top_of_space = self.fromspace ll_assert(self.free <= self.top_of_space, "unexpected growth of GC space usage during collect") return False # out of memory self.fromspace = newspace self.space_size = newsize return True # success def set_max_heap_size(self, size): # Set the maximum semispace size. # The size is rounded down to the next power of two. Also, this is # the size of one semispace only, so actual usage can be the double # during a collection. Finally, note that this will never shrink # an already-allocated heap. if size < 1: size = 1 # actually, the minimum is 8MB in default translations self.max_space_size = sys.maxint // 2 + 1 while self.max_space_size > size: self.max_space_size >>= 1 @classmethod def JIT_minimal_size_in_nursery(cls): return cls.object_minimal_size def collect(self, gen=0): self.debug_check_consistency() self.semispace_collect() # the indirection is required by the fact that collect() is referred # to by the gc transformer, and the default argument would crash # (this is also a hook for the HybridGC) def semispace_collect(self, size_changing=False): debug_start("gc-collect") debug_print() debug_print(".----------- Full collection ------------------") start_usage = self.free - self.tospace debug_print("| used before collection: ", start_usage, "bytes") #start_time = time.time() #llop.debug_print(lltype.Void, 'semispace_collect', int(size_changing)) # Switch the spaces. We copy everything over to the empty space # (self.fromspace at the beginning of the collection), and clear the old # one (self.tospace at the beginning). Their purposes will be reversed # for the next collection. tospace = self.fromspace fromspace = self.tospace self.fromspace = fromspace self.tospace = tospace self.top_of_space = tospace + self.space_size scan = self.free = tospace self.starting_full_collect() self.collect_roots() self.copy_pending_finalizers(self.copy) scan = self.scan_copied(scan) if self.objects_with_light_finalizers.non_empty(): self.deal_with_objects_with_light_finalizers() if self.objects_with_finalizers.non_empty(): scan = self.deal_with_objects_with_finalizers(scan) if self.objects_with_weakrefs.non_empty(): self.invalidate_weakrefs() self.update_objects_with_id() self.finished_full_collect() self.debug_check_consistency() if not size_changing: llarena.arena_reset(fromspace, self.space_size, True) self.record_red_zone() self.execute_finalizers() #llop.debug_print(lltype.Void, 'collected', self.space_size, size_changing, self.top_of_space - self.free) if have_debug_prints(): #end_time = time.time() #elapsed_time = end_time - start_time #self.total_collection_time += elapsed_time self.total_collection_count += 1 #total_program_time = end_time - self.program_start_time end_usage = self.free - self.tospace debug_print("| used after collection: ", end_usage, "bytes") debug_print("| freed: ", start_usage - end_usage, "bytes") debug_print("| size of each semispace: ", self.space_size, "bytes") debug_print("| fraction of semispace now used: ", end_usage * 100.0 / self.space_size, "%") #ct = self.total_collection_time cc = self.total_collection_count debug_print("| number of semispace_collects: ", cc) #debug_print("| i.e.: ", # cc / total_program_time, "per second") #debug_print("| total time in semispace_collect: ", # ct, "seconds") #debug_print("| i.e.: ", # ct * 100.0 / total_program_time, "%") debug_print("`----------------------------------------------") debug_stop("gc-collect") def starting_full_collect(self): pass # hook for the HybridGC def finished_full_collect(self): pass # hook for the HybridGC def record_red_zone(self): # red zone: if the space is more than 80% full, the next collection # should double its size. If it is more than 66% full twice in a row, # then it should double its size too. (XXX adjust) # The goal is to avoid many repeated collection that don't free a lot # of memory each, if the size of the live object set is just below the # size of the space. free_after_collection = self.top_of_space - self.free if free_after_collection > self.space_size // 3: self.red_zone = 0 else: self.red_zone += 1 if free_after_collection < self.space_size // 5: self.red_zone += 1 def get_size_incl_hash(self, obj): size = self.get_size(obj) hdr = self.header(obj) if (hdr.tid & GCFLAG_HASHMASK) == GC_HASH_HASFIELD: size += llmemory.sizeof(lltype.Signed) return size def scan_copied(self, scan): while scan < self.free: curr = scan + self.size_gc_header() self.trace_and_copy(curr) scan += self.size_gc_header() + self.get_size_incl_hash(curr) return scan def collect_roots(self): self.root_walker.walk_roots( SemiSpaceGC._collect_root, # stack roots SemiSpaceGC._collect_root, # static in prebuilt non-gc structures SemiSpaceGC._collect_root) # static in prebuilt gc objects def _collect_root(self, root): root.address[0] = self.copy(root.address[0]) def copy(self, obj): if self.DEBUG: self.debug_check_can_copy(obj) if self.is_forwarded(obj): #llop.debug_print(lltype.Void, obj, "already copied to", self.get_forwarding_address(obj)) return self.get_forwarding_address(obj) else: objsize = self.get_size(obj) newobj = self.make_a_copy(obj, objsize) #llop.debug_print(lltype.Void, obj, "copied to", newobj, # "tid", self.header(obj).tid, # "size", totalsize) self.set_forwarding_address(obj, newobj, objsize) return newobj def _get_object_hash(self, obj, objsize, tid): # Returns the hash of the object, which must not be GC_HASH_NOTTAKEN. gc_hash = tid & GCFLAG_HASHMASK if gc_hash == GC_HASH_HASFIELD: obj = llarena.getfakearenaaddress(obj) return (obj + objsize).signed[0] elif gc_hash == GC_HASH_TAKEN_ADDR: return llmemory.cast_adr_to_int(obj) elif gc_hash == GC_HASH_TAKEN_NURS: return self._compute_current_nursery_hash(obj) else: assert 0, "gc_hash == GC_HASH_NOTTAKEN" def _make_a_copy_with_tid(self, obj, objsize, tid): totalsize = self.size_gc_header() + objsize newaddr = self.free llarena.arena_reserve(newaddr, totalsize) raw_memcopy(obj - self.size_gc_header(), newaddr, totalsize) if tid & GCFLAG_HASHMASK: hash = self._get_object_hash(obj, objsize, tid) llarena.arena_reserve(newaddr + totalsize, llmemory.sizeof(lltype.Signed)) (newaddr + totalsize).signed[0] = hash tid |= GC_HASH_HASFIELD totalsize += llmemory.sizeof(lltype.Signed) self.free += totalsize newhdr = llmemory.cast_adr_to_ptr(newaddr, lltype.Ptr(self.HDR)) newhdr.tid = tid newobj = newaddr + self.size_gc_header() return newobj def make_a_copy(self, obj, objsize): tid = self.header(obj).tid return self._make_a_copy_with_tid(obj, objsize, tid) def trace_and_copy(self, obj): self.trace(obj, self._trace_copy, None) def _trace_copy(self, pointer, ignored): pointer.address[0] = self.copy(pointer.address[0]) def surviving(self, obj): # To use during a collection. Check if the object is currently # marked as surviving the collection. This is equivalent to # self.is_forwarded() for all objects except the nonmoving objects # created by the HybridGC subclass. In all cases, if an object # survives, self.get_forwarding_address() returns its new address. return self.is_forwarded(obj) def is_forwarded(self, obj): return self.header(obj).tid & GCFLAG_FORWARDED != 0 # note: all prebuilt objects also have this flag set def get_forwarding_address(self, obj): tid = self.header(obj).tid if tid & GCFLAG_EXTERNAL: self.visit_external_object(obj) return obj # external or prebuilt objects are "forwarded" # to themselves else: stub = llmemory.cast_adr_to_ptr(obj, self.FORWARDSTUBPTR) return stub.forw def visit_external_object(self, obj): pass # hook for the HybridGC def get_possibly_forwarded_type_id(self, obj): tid = self.header(obj).tid if self.is_forwarded(obj) and not (tid & GCFLAG_EXTERNAL): obj = self.get_forwarding_address(obj) return self.get_type_id(obj) def set_forwarding_address(self, obj, newobj, objsize): # To mark an object as forwarded, we set the GCFLAG_FORWARDED and # overwrite the object with a FORWARDSTUB. Doing so is a bit # long-winded on llarena, but it all melts down to two memory # writes after translation to C. size_gc_header = self.size_gc_header() stubsize = llmemory.sizeof(self.FORWARDSTUB) tid = self.header(obj).tid ll_assert(tid & GCFLAG_EXTERNAL == 0, "unexpected GCFLAG_EXTERNAL") ll_assert(tid & GCFLAG_FORWARDED == 0, "unexpected GCFLAG_FORWARDED") # replace the object at 'obj' with a FORWARDSTUB. hdraddr = obj - size_gc_header llarena.arena_reset(hdraddr, size_gc_header + objsize, False) llarena.arena_reserve(hdraddr, size_gc_header + stubsize) hdr = llmemory.cast_adr_to_ptr(hdraddr, lltype.Ptr(self.HDR)) hdr.tid = tid | GCFLAG_FORWARDED stub = llmemory.cast_adr_to_ptr(obj, self.FORWARDSTUBPTR) stub.forw = newobj def combine(self, typeid16, flags): return llop.combine_ushort(lltype.Signed, typeid16, flags) def get_type_id(self, addr): tid = self.header(addr).tid ll_assert( tid & (GCFLAG_FORWARDED | GCFLAG_EXTERNAL) != GCFLAG_FORWARDED, "get_type_id on forwarded obj") # Non-prebuilt forwarded objects are overwritten with a FORWARDSTUB. # Although calling get_type_id() on a forwarded object works by itself, # we catch it as an error because it's likely that what is then # done with the typeid is bogus. return llop.extract_ushort(llgroup.HALFWORD, tid) def init_gc_object(self, addr, typeid16, flags=0): hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR)) hdr.tid = self.combine(typeid16, flags) def init_gc_object_immortal(self, addr, typeid16, flags=0): hdr = llmemory.cast_adr_to_ptr(addr, lltype.Ptr(self.HDR)) flags |= GCFLAG_EXTERNAL | GCFLAG_FORWARDED | GC_HASH_TAKEN_ADDR hdr.tid = self.combine(typeid16, flags) # immortal objects always have GCFLAG_FORWARDED set; # see get_forwarding_address(). def deal_with_objects_with_light_finalizers(self): """ This is a much simpler version of dealing with finalizers and an optimization - we can reasonably assume that those finalizers don't do anything fancy and *just* call them. Among other things they won't resurrect objects """ new_objects = self.AddressStack() while self.objects_with_light_finalizers.non_empty(): obj = self.objects_with_light_finalizers.pop() if self.surviving(obj): new_objects.append(self.get_forwarding_address(obj)) else: self.call_destructor(obj) self.objects_with_light_finalizers.delete() self.objects_with_light_finalizers = new_objects def deal_with_objects_with_finalizers(self, scan): # walk over list of objects with finalizers # if it is not copied, add it to the list of to-be-called finalizers # and copy it, to me make the finalizer runnable # We try to run the finalizers in a "reasonable" order, like # CPython does. The details of this algorithm are in # pypy/doc/discussion/finalizer-order.txt. new_with_finalizer = self.AddressDeque() marked = self.AddressDeque() pending = self.AddressStack() self.tmpstack = self.AddressStack() while self.objects_with_finalizers.non_empty(): x = self.objects_with_finalizers.popleft() fq_nr = self.objects_with_finalizers.popleft() ll_assert( self._finalization_state(x) != 1, "bad finalization state 1") if self.surviving(x): new_with_finalizer.append(self.get_forwarding_address(x)) new_with_finalizer.append(fq_nr) continue marked.append(x) marked.append(fq_nr) pending.append(x) while pending.non_empty(): y = pending.pop() state = self._finalization_state(y) if state == 0: self._bump_finalization_state_from_0_to_1(y) self.trace(y, self._append_if_nonnull, pending) elif state == 2: self._recursively_bump_finalization_state_from_2_to_3(y) scan = self._recursively_bump_finalization_state_from_1_to_2( x, scan) while marked.non_empty(): x = marked.popleft() fq_nr = marked.popleft() state = self._finalization_state(x) ll_assert(state >= 2, "unexpected finalization state < 2") newx = self.get_forwarding_address(x) if state == 2: from rpython.rtyper.lltypesystem import rffi fq_index = rffi.cast(lltype.Signed, fq_nr) self.mark_finalizer_to_run(fq_index, newx) # we must also fix the state from 2 to 3 here, otherwise # we leave the GCFLAG_FINALIZATION_ORDERING bit behind # which will confuse the next collection self._recursively_bump_finalization_state_from_2_to_3(x) else: new_with_finalizer.append(newx) new_with_finalizer.append(fq_nr) self.tmpstack.delete() pending.delete() marked.delete() self.objects_with_finalizers.delete() self.objects_with_finalizers = new_with_finalizer return scan def _append_if_nonnull(pointer, stack): stack.append(pointer.address[0]) _append_if_nonnull = staticmethod(_append_if_nonnull) def _finalization_state(self, obj): if self.surviving(obj): newobj = self.get_forwarding_address(obj) hdr = self.header(newobj) if hdr.tid & GCFLAG_FINALIZATION_ORDERING: return 2 else: return 3 else: hdr = self.header(obj) if hdr.tid & GCFLAG_FINALIZATION_ORDERING: return 1 else: return 0 def _bump_finalization_state_from_0_to_1(self, obj): ll_assert( self._finalization_state(obj) == 0, "unexpected finalization state != 0") hdr = self.header(obj) hdr.tid |= GCFLAG_FINALIZATION_ORDERING def _recursively_bump_finalization_state_from_2_to_3(self, obj): ll_assert( self._finalization_state(obj) == 2, "unexpected finalization state != 2") newobj = self.get_forwarding_address(obj) pending = self.tmpstack ll_assert(not pending.non_empty(), "tmpstack not empty") pending.append(newobj) while pending.non_empty(): y = pending.pop() hdr = self.header(y) if hdr.tid & GCFLAG_FINALIZATION_ORDERING: # state 2 ? hdr.tid &= ~GCFLAG_FINALIZATION_ORDERING # change to state 3 self.trace(y, self._append_if_nonnull, pending) def _recursively_bump_finalization_state_from_1_to_2(self, obj, scan): # recursively convert objects from state 1 to state 2. # Note that copy() copies all bits, including the # GCFLAG_FINALIZATION_ORDERING. The mapping between # state numbers and the presence of this bit was designed # for the following to work :-) self.copy(obj) return self.scan_copied(scan) def invalidate_weakrefs(self): # walk over list of objects that contain weakrefs # if the object it references survives then update the weakref # otherwise invalidate the weakref new_with_weakref = self.AddressStack() while self.objects_with_weakrefs.non_empty(): obj = self.objects_with_weakrefs.pop() if not self.surviving(obj): continue # weakref itself dies obj = self.get_forwarding_address(obj) offset = self.weakpointer_offset(self.get_type_id(obj)) pointing_to = (obj + offset).address[0] # XXX I think that pointing_to cannot be NULL here if pointing_to: if self.surviving(pointing_to): (obj + offset ).address[0] = self.get_forwarding_address(pointing_to) new_with_weakref.append(obj) else: (obj + offset).address[0] = NULL self.objects_with_weakrefs.delete() self.objects_with_weakrefs = new_with_weakref def _is_external(self, obj): return (self.header(obj).tid & GCFLAG_EXTERNAL) != 0 def _is_in_the_space(self, obj): return self.tospace <= obj < self.free def debug_check_object(self, obj): """Check the invariants about 'obj' that should be true between collections.""" tid = self.header(obj).tid if tid & GCFLAG_EXTERNAL: ll_assert(tid & GCFLAG_FORWARDED != 0, "bug: external+!forwarded") ll_assert(not (self.tospace <= obj < self.free), "external flag but object inside the semispaces") else: ll_assert(not (tid & GCFLAG_FORWARDED), "bug: !external+forwarded") ll_assert(self.tospace <= obj < self.free, "!external flag but object outside the semispaces") ll_assert(not (tid & GCFLAG_FINALIZATION_ORDERING), "unexpected GCFLAG_FINALIZATION_ORDERING") def debug_check_can_copy(self, obj): ll_assert(not (self.tospace <= obj < self.free), "copy() on already-copied object") STATISTICS_NUMBERS = 0 def is_in_nursery(self, addr): # overridden in generation.py. return False def _compute_current_nursery_hash(self, obj): # overridden in generation.py. raise AssertionError("should not be called") def identityhash(self, gcobj): # The following loop should run at most twice. while 1: obj = llmemory.cast_ptr_to_adr(gcobj) hdr = self.header(obj) if hdr.tid & GCFLAG_HASHMASK: break # It's the first time we ask for a hash, and it's not an # external object. Shrink the top of space by the extra # hash word that will be needed after a collect. shrunk_top = self.top_of_space - llmemory.sizeof(lltype.Signed) if shrunk_top < self.free: # Cannot shrink! Do a collection, asking for at least # one word of free space, and try again. May raise # MemoryError. Obscure: not called directly, but # across an llop, to make sure that there is the # correct push_roots/pop_roots around the call... llop.gc_obtain_free_space(llmemory.Address, llmemory.sizeof(lltype.Signed)) continue else: # Now we can have side-effects: lower the top of space # and set one of the GC_HASH_TAKEN_xxx flags. self.top_of_space = shrunk_top if self.is_in_nursery(obj): hdr.tid |= GC_HASH_TAKEN_NURS else: hdr.tid |= GC_HASH_TAKEN_ADDR break # Now we can return the result objsize = self.get_size(obj) return self._get_object_hash(obj, objsize, hdr.tid) def track_heap_parent(self, obj, parent): addr = obj.address[0] parent_idx = llop.get_member_index(lltype.Signed, self.get_type_id(parent)) idx = llop.get_member_index(lltype.Signed, self.get_type_id(addr)) self._ll_typeid_map[parent_idx].links[idx] += 1 self.track_heap(addr) def track_heap(self, adr): if self._tracked_dict.contains(adr): return self._tracked_dict.add(adr) idx = llop.get_member_index(lltype.Signed, self.get_type_id(adr)) self._ll_typeid_map[idx].count += 1 totsize = self.get_size(adr) + self.size_gc_header() self._ll_typeid_map[idx].size += llmemory.raw_malloc_usage(totsize) self.trace(adr, self.track_heap_parent, adr) @staticmethod def _track_heap_root(obj, self): self.track_heap(obj) def heap_stats(self): self._tracked_dict = self.AddressDict() max_tid = self.root_walker.gcdata.max_type_id ll_typeid_map = lltype.malloc(ARRAY_TYPEID_MAP, max_tid, zero=True) for i in range(max_tid): ll_typeid_map[i] = lltype.malloc(TYPEID_MAP, max_tid, zero=True) self._ll_typeid_map = ll_typeid_map self._tracked_dict.add(llmemory.cast_ptr_to_adr(ll_typeid_map)) i = 0 while i < max_tid: self._tracked_dict.add(llmemory.cast_ptr_to_adr(ll_typeid_map[i])) i += 1 self.enumerate_all_roots(SemiSpaceGC._track_heap_root, self) self._ll_typeid_map = lltype.nullptr(ARRAY_TYPEID_MAP) self._tracked_dict.delete() return ll_typeid_map
from rpython.rtyper.lltypesystem import lltype from rpython.translator.unsimplify import varoftype from rpython.flowspace.model import Constant, SpaceOperation from rpython.jit.codewriter.jtransform import Transformer, NotSupported from rpython.jit.codewriter.flatten import GraphFlattener from rpython.jit.codewriter.format import assert_format from rpython.jit.codewriter.test.test_flatten import fake_regallocs from rpython.jit.metainterp.history import AbstractDescr # ____________________________________________________________ FIXEDLIST = lltype.Ptr(lltype.GcArray(lltype.Signed)) FIXEDPTRLIST = lltype.Ptr(lltype.GcArray(FIXEDLIST)) VARLIST = lltype.Ptr(lltype.GcStruct('VARLIST', ('length', lltype.Signed), ('items', FIXEDLIST), adtmeths={"ITEM": lltype.Signed})) class FakeCPU: class arraydescrof(AbstractDescr): def __init__(self, ARRAY): assert ARRAY.OF != lltype.Void self.ARRAY = ARRAY def __repr__(self): return '<ArrayDescr>' class fielddescrof(AbstractDescr): def __init__(self, STRUCT, fieldname): self.STRUCT = STRUCT self.fieldname = fieldname def __repr__(self): return '<FieldDescr %s>' % self.fieldname
from rpython.rtyper.lltypesystem import lltype, llmemory, llarena, rffi from rpython.rtyper.lltypesystem.lloperation import llop from rpython.rlib.debug import ll_assert from rpython.memory.gcheader import GCHeaderBuilder from rpython.memory.support import DEFAULT_CHUNK_SIZE from rpython.memory.support import get_address_stack, get_address_deque from rpython.memory.support import AddressDict, null_address_dict from rpython.rtyper.lltypesystem.llmemory import NULL, raw_malloc_usage from rpython.rtyper.annlowlevel import cast_adr_to_nongc_instance TYPEID_MAP = lltype.GcStruct('TYPEID_MAP', ('count', lltype.Signed), ('size', lltype.Signed), ('links', lltype.Array(lltype.Signed))) ARRAY_TYPEID_MAP = lltype.GcArray(lltype.Ptr(TYPEID_MAP)) class GCBase(object): _alloc_flavor_ = "raw" moving_gc = False needs_write_barrier = False malloc_zero_filled = False prebuilt_gc_objects_are_static_roots = True can_usually_pin_objects = False object_minimal_size = 0 gcflag_extra = 0 # or a real GC flag that is always 0 when not collecting def __init__(self, config, chunk_size=DEFAULT_CHUNK_SIZE, translated_to_c=True): self.gcheaderbuilder = GCHeaderBuilder(self.HDR)
from rpython.rlib import rgc from rpython.rtyper.lltypesystem import lltype, llmemory, rffi from rpython.rtyper.lltypesystem.lloperation import llop from rpython.jit.backend.llsupport.symbolic import WORD GCREFTRACER = lltype.GcStruct('GCREFTRACER', ('array_base_addr', lltype.Signed), ('array_length', lltype.Signed), rtti=True) def gcrefs_trace(gc, obj_addr, callback, arg): obj = llmemory.cast_adr_to_ptr(obj_addr, lltype.Ptr(GCREFTRACER)) i = 0 length = obj.array_length addr = obj.array_base_addr while i < length: p = rffi.cast(llmemory.Address, addr + i * WORD) gc._trace_callback(callback, arg, p) i += 1 lambda_gcrefs_trace = lambda: gcrefs_trace def make_framework_tracer(array_base_addr, gcrefs): # careful about the order here: the allocation of the GCREFTRACER # can trigger a GC. So we must write the gcrefs into the raw # array only afterwards... rgc.register_custom_trace_hook(GCREFTRACER, lambda_gcrefs_trace) length = len(gcrefs)