def test_quasiimmut_seen_consts(self): h = HeapCache() box1 = ConstPtr(rffi.cast(llmemory.GCREF, 1)) box2 = ConstPtr(rffi.cast(llmemory.GCREF, 1)) box3 = ConstPtr(rffi.cast(llmemory.GCREF, 1)) box4 = ConstPtr(rffi.cast(llmemory.GCREF, 1)) assert not h.is_quasi_immut_known(descr1, box1) assert not h.is_quasi_immut_known(descr1, box2) assert not h.is_quasi_immut_known(descr2, box3) assert not h.is_quasi_immut_known(descr2, box4) h.quasi_immut_now_known(descr1, box1) assert h.is_quasi_immut_known(descr1, box1) assert h.is_quasi_immut_known(descr1, box2) assert not h.is_quasi_immut_known(descr2, box3) assert not h.is_quasi_immut_known(descr2, box4) h.quasi_immut_now_known(descr2, box3) assert h.is_quasi_immut_known(descr1, box1) assert h.is_quasi_immut_known(descr1, box2) assert h.is_quasi_immut_known(descr2, box3) assert h.is_quasi_immut_known(descr2, box4) # invalidate the descr1 cache vbox1 = RefFrontendOp(1) vbox2 = RefFrontendOp(2) h.setfield(vbox1, vbox2, descr1) assert not h.is_quasi_immut_known(descr1, box1) assert not h.is_quasi_immut_known(descr1, box2) # a call invalidates everything h.invalidate_caches( rop.CALL_N, FakeCallDescr(FakeEffectinfo.EF_CAN_RAISE), []) assert not h.is_quasi_immut_known(descr2, box3) assert not h.is_quasi_immut_known(descr2, box4)
def test_replace_box_with_const(self): h = HeapCache() box1 = RefFrontendOp(1) box2 = RefFrontendOp(2) box3 = RefFrontendOp(3) c_box3 = ConstPtr(ConstPtr.value) h.setfield(box1, box2, descr1) h.setfield(box1, box3, descr2) h.setfield(box2, box3, descr3) h.replace_box(box3, c_box3) assert h.getfield(box1, descr1) is box2 assert c_box3.same_constant(h.getfield(box1, descr2)) assert c_box3.same_constant(h.getfield(box2, descr3))
def make_args_for_op(op, a, b): n = opname[op] if n[0:3] == 'INT' or n[0:4] == 'UINT': arg1 = ConstInt(a) arg2 = ConstInt(b) elif n[0:5] == 'FLOAT': arg1 = constfloat(float(a)) arg2 = constfloat(float(b)) elif n[0:3] == 'PTR': arg1 = ConstPtr(rffi.cast(llmemory.GCREF, a)) arg2 = ConstPtr(rffi.cast(llmemory.GCREF, b)) else: raise NotImplementedError("Don't know how to make args for " + n) return arg1, arg2
def test_record_constptrs(self): class MyFakeCPU(object): def cast_adr_to_int(self, adr): assert adr == "some fake address" return 43 class MyFakeGCRefList(object): def get_address_of_gcref(self, s_gcref1): assert s_gcref1 == s_gcref return "some fake address" S = lltype.GcStruct('S') s = lltype.malloc(S) s_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, s) v_random_box = BoxPtr() v_result = BoxInt() operations = [ ResOperation(rop.PTR_EQ, [v_random_box, ConstPtr(s_gcref)], v_result), ] gc_ll_descr = self.gc_ll_descr gc_ll_descr.gcrefs = MyFakeGCRefList() gcrefs = [] operations = get_deep_immutable_oplist(operations) operations2 = gc_ll_descr.rewrite_assembler(MyFakeCPU(), operations, gcrefs) assert operations2 == operations assert gcrefs == [s_gcref]
def test_nonstandard_virtualizable_const(self): h = HeapCache() # rare but not impossible situation for some interpreters: we have a # *constant* nonstandard virtualizable c_box = ConstPtr(ConstPtr.value) h.nonstandard_virtualizables_now_known(c_box) # should not crash assert not h.is_known_nonstandard_virtualizable(c_box)
def __init__(self, cpu, const_pointers): size = len(const_pointers) # check that there are any moving object (i.e. chaning pointers). # Otherwise there is no reason for an instance of this class. assert size > 0 # # prepare GC array to hold the pointers that may change self.ptr_array = lltype.malloc(MovableObjectTracker.ptr_array_type, size) self.ptr_array_descr = cpu.arraydescrof( MovableObjectTracker.ptr_array_type) self.ptr_array_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, self.ptr_array) # use always the same ConstPtr to access the array # (easer to read JIT trace) self.const_ptr_gcref_array = ConstPtr(self.ptr_array_gcref) # # assign each pointer an index and put the pointer into the GC array. # as pointers and addresses are not a good key to use before translation # ConstPtrs are used as the key for the dict. self._indexes = {} for index in range(size): ptr = const_pointers[index] self._indexes[ptr] = index self.ptr_array[index] = ptr.value
def get_structptr_var(self, r, must_have_vtable=False, type=lltype.Struct, array_of_structs=False): while True: ptrvars = self._choose_ptr_vars(self.ptrvars, type, array_of_structs) if ptrvars and r.random() < 0.8: v, S = r.choice(ptrvars) else: prebuilt_ptr_consts = self._choose_ptr_vars( self.prebuilt_ptr_consts, type, array_of_structs) if prebuilt_ptr_consts and r.random() < 0.7: v, S = r.choice(prebuilt_ptr_consts) else: if type is lltype.Struct: # create a new constant structure must_have_vtable = must_have_vtable or r.random() < 0.5 p = self.get_random_structure( r, has_vtable=must_have_vtable) else: # create a new constant array p = self.get_random_array( r, must_be_array_of_structs=array_of_structs) S = lltype.typeOf(p).TO v = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, p)) self.prebuilt_ptr_consts.append( (v, S, self.field_values(p))) if not (must_have_vtable and S._names[0] != 'parent'): break return v, S
def test_ResumeDataLoopMemo_refs(): memo = ResumeDataLoopMemo(FakeMetaInterpStaticData()) const = ConstPtr(demo55o) tagged = memo.getconst(const) index, tagbits = untag(tagged) assert tagbits == TAGCONST assert memo.consts[index - TAG_CONST_OFFSET] is const tagged = memo.getconst(ConstPtr(demo55o)) index2, tagbits = untag(tagged) assert tagbits == TAGCONST assert index2 == index tagged = memo.getconst(ConstPtr(demo66o)) index3, tagbits = untag(tagged) assert tagbits == TAGCONST assert index3 != index tagged = memo.getconst(CONST_NULL) assert tagged == NULLREF
def constbox(v): if v.type == INT: return ConstInt(getint(v)) if v.type == FLOAT: return ConstFloat(getfloatstorage(v)) if v.type == REF: return ConstPtr(getref_base(v)) assert 0, v.type
def constant_from_op(op): if op.type == 'i': return ConstInt(op.getint()) elif op.type == 'r': return ConstPtr(op.getref_base()) else: assert op.type == 'f' return ConstFloat(op.getfloatstorage())
def get_current_constant_fieldvalue(self): struct = self.struct fielddescr = self.fielddescr if self.fielddescr.is_pointer_field(): return ConstPtr(self.cpu.bh_getfield_gc_r(struct, fielddescr)) elif self.fielddescr.is_float_field(): return ConstFloat(self.cpu.bh_getfield_gc_f(struct, fielddescr)) else: return ConstInt(self.cpu.bh_getfield_gc_i(struct, fielddescr))
def test_known_class_generalization(self): knownclass1 = OptValue(BoxPtr()) knownclass1.make_constant_class(ConstPtr(self.someptr1), 0) info1 = NotVirtualStateInfo(knownclass1) info1.position = 0 knownclass2 = OptValue(BoxPtr()) knownclass2.make_constant_class(ConstPtr(self.someptr1), 0) info2 = NotVirtualStateInfo(knownclass2) info2.position = 0 assert info1.generalization_of(info2, {}, {}) assert info2.generalization_of(info1, {}, {}) knownclass3 = OptValue(BoxPtr()) knownclass3.make_constant_class(ConstPtr(self.someptr2), 0) info3 = NotVirtualStateInfo(knownclass3) info3.position = 0 assert not info1.generalization_of(info3, {}, {}) assert not info2.generalization_of(info3, {}, {}) assert not info3.generalization_of(info2, {}, {}) assert not info3.generalization_of(info1, {}, {})
def test_arrayitems(self): TP = lltype.GcArray(lltype.Signed) ofs = symbolic.get_field_token(TP, 'length', False)[0] itemsofs = symbolic.get_field_token(TP, 'items', False)[0] descr = self.cpu.arraydescrof(TP) res = self.execute_operation(rop.NEW_ARRAY, [ConstInt(10)], 'ref', descr) resbuf = self._resbuf(res) assert resbuf[ofs / WORD] == 10 self.execute_operation( rop.SETARRAYITEM_GC, [InputArgRef(res), ConstInt(2), InputArgInt(38)], 'void', descr) assert resbuf[itemsofs / WORD + 2] == 38 self.execute_operation( rop.SETARRAYITEM_GC, [InputArgRef(res), InputArgInt(3), InputArgInt(42)], 'void', descr) assert resbuf[itemsofs / WORD + 3] == 42 r = self.execute_operation( rop.GETARRAYITEM_GC_I, [InputArgRef(res), ConstInt(2)], 'int', descr) assert r == 38 r = self.execute_operation( rop.GETARRAYITEM_GC_I, [ConstPtr(res), InputArgInt(2)], 'int', descr) assert r == 38 r = self.execute_operation(rop.GETARRAYITEM_GC_I, [ConstPtr(res), ConstInt(2)], 'int', descr) assert r == 38 r = self.execute_operation( rop.GETARRAYITEM_GC_I, [InputArgRef(res), InputArgInt(2)], 'int', descr) assert r == 38 r = self.execute_operation( rop.GETARRAYITEM_GC_I, [InputArgRef(res), InputArgInt(3)], 'int', descr) assert r == 42
def wrap_constant(value): if lltype.typeOf(value) == lltype.Signed: return ConstInt(value) elif isinstance(value, bool): return ConstInt(int(value)) elif lltype.typeOf(value) == longlong.FLOATSTORAGE: return ConstFloat(value) elif isinstance(value, float): return ConstFloat(longlong.getfloatstorage(value)) else: assert lltype.typeOf(value) == llmemory.GCREF return ConstPtr(value)
def gen_guard(self, builder, r): if r.random() < 0.5: return GuardClassOperation.gen_guard(self, builder, r) else: NULL = lltype.nullptr(llmemory.GCREF.TO) op = ResOperation(rop.SAME_AS_R, [ConstPtr(NULL)]) builder.loop.operations.append(op) v2, S2 = builder.get_structptr_var(r, must_have_vtable=True) vtable2 = S2._hints['vtable']._as_ptr() c_vtable2 = ConstInt(ptr2int(vtable2)) op = ResOperation(self.opnum, [op, c_vtable2], None) return op, False
def get_string(self, builder, r): current = getattr(builder, self.builder_cache) if current and r.random() < .8: v_string = r.choice(current) string = getref(self.ptr, v_string) else: string = self.alloc(getint(builder.get_index(500, r))) v_string = ConstPtr(lltype.cast_opaque_ptr(llmemory.GCREF, string)) current.append(v_string) for i in range(len(string.chars)): char = r.random_integer() % self.max string.chars[i] = lltype.cast_primitive(self.primitive, char) return v_string
def gen_guard(self, builder, r): if r.random() < 0.5: return GuardClassOperation.gen_guard(self, builder, r) else: v = BoxPtr(lltype.nullptr(llmemory.GCREF.TO)) op = ResOperation(rop.SAME_AS, [ConstPtr(v.value)], v) builder.loop.operations.append(op) v2, S2 = builder.get_structptr_var(r, must_have_vtable=True) vtable2 = S2._hints['vtable']._as_ptr() c_vtable2 = ConstAddr(llmemory.cast_ptr_to_adr(vtable2), builder.cpu) op = ResOperation(self.opnum, [v, c_vtable2], None) return op, False
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 _untag(self, tagged): tag, v = untag(tagged) if tag == TAGBOX: return self._get(v) elif tag == TAGINT: return ConstInt(v + SMALL_INT_START) elif tag == TAGCONSTPTR: return ConstPtr(self.trace._refs[v]) elif tag == TAGCONSTOTHER: if v & 1: return ConstFloat(self.trace._floats[v >> 1]) else: return ConstInt(self.trace._bigints[v >> 1]) else: assert False
def test_bug_setfield_64bit(self): if WORD == 4: py.test.skip("only for 64 bits") TP = lltype.GcStruct('S', ('i', lltype.Signed)) ofsi = self.cpu.fielddescrof(TP, 'i') for i in range(500): p = lltype.malloc(TP) addr = rffi.cast(lltype.Signed, p) if fits_in_32bits(addr): break # fitting in 32 bits, good else: py.test.skip("cannot get a 32-bit pointer") res = ConstPtr(rffi.cast(llmemory.GCREF, addr)) self.execute_operation(rop.SETFIELD_RAW, [res, ConstInt(3**33)], 'void', ofsi) assert p.i == 3**33
def test_simple_read(): #b1, b2, b3 = [BoxInt(), InputArgRef(), BoxInt()] c1, c2, c3 = [ConstInt(111), ConstInt(222), ConstInt(333)] storage = Storage() storage.rd_consts = [c1, c2, c3] numb = Numbering( [ 3, tag(0, TAGBOX), tagconst(0), NULLREF, tag(0, TAGBOX), tag(1, TAGBOX) ] + [tagconst(1), tagconst(2)] + [tag(0, TAGBOX), tag(1, TAGBOX), tag(2, TAGBOX)]) storage.rd_numb = numb # cpu = MyCPU([42, gcref1, -66]) metainterp = MyMetaInterp(cpu) reader = ResumeDataDirectReader(metainterp, storage, "deadframe") _next_section(reader, 42, 111, gcrefnull, 42, gcref1) _next_section(reader, 222, 333) _next_section(reader, 42, gcref1, -66) # reader = ResumeDataBoxReader(storage, "deadframe", metainterp) bi, br, bf = [None] * 3, [None] * 2, [None] * 0 bh = MyBlackholeInterp([ lltype.Signed, lltype.Signed, llmemory.GCREF, lltype.Signed, llmemory.GCREF ]) bh.fake_consume_boxes(reader, bi, br, bf) b1s = reader.liveboxes[0] b2s = reader.liveboxes[1] assert_same(bi, [b1s, ConstInt(111), b1s]) assert_same(br, [ConstPtr(gcrefnull), b2s]) bi, br, bf = [None] * 2, [None] * 0, [None] * 0 bh = MyBlackholeInterp([lltype.Signed, lltype.Signed]) bh.fake_consume_boxes(reader, bi, br, bf) assert_same(bi, [ConstInt(222), ConstInt(333)]) bi, br, bf = [None] * 2, [None] * 1, [None] * 0 bh = MyBlackholeInterp([lltype.Signed, llmemory.GCREF, lltype.Signed]) bh.fake_consume_boxes(reader, bi, br, bf) b3s = reader.liveboxes[2] assert_same(bi, [b1s, b3s]) assert_same(br, [b2s])
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 setup_class(cls): if cls.runappdirect: py.test.skip("Can't run this test with -A") w_f = cls.space.appexec([], """(): def function(): pass return function """) cls.w_f = w_f ll_code = cast_instance_to_base_ptr(w_f.code) code_gcref = lltype.cast_opaque_ptr(llmemory.GCREF, ll_code) logger = Logger(MockSD()) oplist = parse(""" [i1, i2, p2] i3 = int_add(i1, i2) debug_merge_point(0, 0, 0, 0, 0, ConstPtr(ptr0)) guard_nonnull(p2) [] guard_true(i3) [] """, namespace={ 'ptr0': code_gcref }).operations greenkey = [ConstInt(0), ConstInt(0), ConstPtr(code_gcref)] offset = {} for i, op in enumerate(oplist): if i != 1: offset[op] = i class FailDescr(BasicFailDescr): def get_jitcounter_hash(self): from rpython.rlib.rarithmetic import r_uint return r_uint(13) oplist[-1].setdescr(FailDescr()) oplist[-2].setdescr(FailDescr()) token = JitCellToken() token.number = 0 di_loop = JitDebugInfo(MockJitDriverSD, logger, token, oplist, 'loop', greenkey) di_loop_optimize = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(), oplist, 'loop', greenkey) di_loop.asminfo = AsmInfo(offset, 0x42, 12) di_bridge = JitDebugInfo(MockJitDriverSD, logger, JitCellToken(), oplist, 'bridge', fail_descr=FailDescr()) di_bridge.asminfo = AsmInfo(offset, 0, 0) def interp_on_compile(): di_loop.oplist = cls.oplist pypy_hooks.after_compile(di_loop) def interp_on_compile_bridge(): pypy_hooks.after_compile_bridge(di_bridge) def interp_on_optimize(): di_loop_optimize.oplist = cls.oplist pypy_hooks.before_compile(di_loop_optimize) def interp_on_abort(): pypy_hooks.on_abort(Counters.ABORT_TOO_LONG, pypyjitdriver, greenkey, 'blah', Logger(MockSD), []) space = cls.space cls.w_on_compile = space.wrap(interp2app(interp_on_compile)) cls.w_on_compile_bridge = space.wrap( interp2app(interp_on_compile_bridge)) cls.w_on_abort = space.wrap(interp2app(interp_on_abort)) cls.w_int_add_num = space.wrap(rop.INT_ADD) cls.w_dmp_num = space.wrap(rop.DEBUG_MERGE_POINT) cls.w_on_optimize = space.wrap(interp2app(interp_on_optimize)) cls.orig_oplist = oplist cls.w_sorted_keys = space.wrap(sorted(Counters.counter_names))
class GcRewriterAssembler(object): """ This class performs the following rewrites on the list of operations: - Turn all NEW_xxx to either a CALL_R/CHECK_MEMORY_ERROR, or a CALL_MALLOC_NURSERY, followed by SETFIELDs in order to initialize their GC fields. The two advantages of CALL_MALLOC_NURSERY is that it inlines the common path, and we need only one such operation to allocate several blocks of memory at once. - Add COND_CALLs to the write barrier before SETFIELD_GC and SETARRAYITEM_GC operations. '_write_barrier_applied' contains a dictionary of variable -> None. If a variable is in the dictionary, next setfields can be called without a write barrier. The idea is that an object that was freshly allocated or already write_barrier'd don't need another write_barrier if there was no potentially collecting resop inbetween. """ _previous_size = -1 _op_malloc_nursery = None _v_last_malloced_nursery = None c_zero = ConstInt(0) c_null = ConstPtr(lltype.nullptr(llmemory.GCREF.TO)) def __init__(self, gc_ll_descr, cpu): self.gc_ll_descr = gc_ll_descr self.cpu = cpu self._newops = [] self._known_lengths = {} self._write_barrier_applied = {} self._delayed_zero_setfields = {} self.last_zero_arrays = [] self._setarrayitems_occurred = {} # {box: {set-of-indexes}} def remember_known_length(self, op, val): self._known_lengths[op] = val def remember_setarrayitem_occured(self, op, index): op = self.get_box_replacement(op) try: subs = self._setarrayitems_occurred[op] except KeyError: subs = {} self._setarrayitems_occurred[op] = subs subs[index] = None def setarrayitems_occurred(self, op): return self._setarrayitems_occurred[self.get_box_replacement(op)] def known_length(self, op, default): return self._known_lengths.get(op, default) def delayed_zero_setfields(self, op): op = self.get_box_replacement(op) try: d = self._delayed_zero_setfields[op] except KeyError: d = {} self._delayed_zero_setfields[op] = d return d def get_box_replacement(self, op, allow_none=False): if allow_none and op is None: return None # for failargs while op.get_forwarded(): op = op.get_forwarded() return op def emit_op(self, op): op = self.get_box_replacement(op) orig_op = op replaced = False opnum = op.getopnum() keep = (opnum == rop.JIT_DEBUG) for i in range(op.numargs()): orig_arg = op.getarg(i) arg = self.get_box_replacement(orig_arg) if isinstance(arg, ConstPtr) and bool(arg.value) and not keep: arg = self.remove_constptr(arg) if orig_arg is not arg: if not replaced: op = op.copy_and_change(opnum) orig_op.set_forwarded(op) replaced = True op.setarg(i, arg) if rop.is_guard(opnum): if not replaced: op = op.copy_and_change(opnum) orig_op.set_forwarded(op) op.setfailargs( [self.get_box_replacement(a, True) for a in op.getfailargs()]) if rop.is_guard(opnum) or opnum == rop.FINISH: llref = cast_instance_to_gcref(op.getdescr()) self.gcrefs_output_list.append(llref) self._newops.append(op) def replace_op_with(self, op, newop): assert not op.get_forwarded() op.set_forwarded(newop) def handle_setarrayitem(self, op): itemsize, basesize, _ = unpack_arraydescr(op.getdescr()) ptr_box = op.getarg(0) index_box = op.getarg(1) value_box = op.getarg(2) self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box, itemsize, itemsize, basesize) def emit_gc_store_or_indexed(self, op, ptr_box, index_box, value_box, itemsize, factor, offset): factor, offset, index_box = \ self._emit_mul_if_factor_offset_not_supported(index_box, factor, offset) # if index_box is None: args = [ptr_box, ConstInt(offset), value_box, ConstInt(itemsize)] newload = ResOperation(rop.GC_STORE, args) else: args = [ ptr_box, index_box, value_box, ConstInt(factor), ConstInt(offset), ConstInt(itemsize) ] newload = ResOperation(rop.GC_STORE_INDEXED, args) if op is not None: self.replace_op_with(op, newload) else: self.emit_op(newload) def handle_getarrayitem(self, op): itemsize, ofs, sign = unpack_arraydescr(op.getdescr()) ptr_box = op.getarg(0) index_box = op.getarg(1) self.emit_gc_load_or_indexed(op, ptr_box, index_box, itemsize, itemsize, ofs, sign) def _emit_mul_if_factor_offset_not_supported(self, index_box, factor, offset): factor, offset, new_index_box, emit = cpu_simplify_scale( self.cpu, index_box, factor, offset) if emit: self.emit_op(new_index_box) return factor, offset, new_index_box def emit_gc_load_or_indexed(self, op, ptr_box, index_box, itemsize, factor, offset, sign, type='i'): factor, offset, index_box = \ self._emit_mul_if_factor_offset_not_supported(index_box, factor, offset) # if sign: # encode signed into the itemsize value itemsize = -itemsize # optype = type if op is not None: optype = op.type if index_box is None: args = [ptr_box, ConstInt(offset), ConstInt(itemsize)] newload = ResOperation(OpHelpers.get_gc_load(optype), args) else: args = [ ptr_box, index_box, ConstInt(factor), ConstInt(offset), ConstInt(itemsize) ] newload = ResOperation(OpHelpers.get_gc_load_indexed(optype), args) if op is None: self.emit_op(newload) else: self.replace_op_with(op, newload) return newload def transform_to_gc_load(self, op): NOT_SIGNED = 0 CINT_ZERO = ConstInt(0) opnum = op.getopnum() if rop.is_getarrayitem(opnum) or \ opnum in (rop.GETARRAYITEM_RAW_I, rop.GETARRAYITEM_RAW_F): self.handle_getarrayitem(op) elif opnum in (rop.SETARRAYITEM_GC, rop.SETARRAYITEM_RAW): self.handle_setarrayitem(op) elif opnum == rop.RAW_STORE: itemsize, ofs, _ = unpack_arraydescr(op.getdescr()) ptr_box = op.getarg(0) index_box = op.getarg(1) value_box = op.getarg(2) self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box, itemsize, 1, ofs) elif opnum in (rop.RAW_LOAD_I, rop.RAW_LOAD_F): itemsize, ofs, sign = unpack_arraydescr(op.getdescr()) ptr_box = op.getarg(0) index_box = op.getarg(1) self.emit_gc_load_or_indexed(op, ptr_box, index_box, itemsize, 1, ofs, sign) elif opnum in (rop.GETINTERIORFIELD_GC_I, rop.GETINTERIORFIELD_GC_R, rop.GETINTERIORFIELD_GC_F): ofs, itemsize, fieldsize, sign = unpack_interiorfielddescr( op.getdescr()) ptr_box = op.getarg(0) index_box = op.getarg(1) self.emit_gc_load_or_indexed(op, ptr_box, index_box, fieldsize, itemsize, ofs, sign) elif opnum in (rop.SETINTERIORFIELD_RAW, rop.SETINTERIORFIELD_GC): ofs, itemsize, fieldsize, sign = unpack_interiorfielddescr( op.getdescr()) ptr_box = op.getarg(0) index_box = op.getarg(1) value_box = op.getarg(2) self.emit_gc_store_or_indexed(op, ptr_box, index_box, value_box, fieldsize, itemsize, ofs) elif opnum in (rop.GETFIELD_GC_I, rop.GETFIELD_GC_F, rop.GETFIELD_GC_R, rop.GETFIELD_RAW_I, rop.GETFIELD_RAW_F, rop.GETFIELD_RAW_R): ofs, itemsize, sign = unpack_fielddescr(op.getdescr()) ptr_box = op.getarg(0) if op.getopnum() in (rop.GETFIELD_GC_F, rop.GETFIELD_GC_I, rop.GETFIELD_GC_R): # See test_zero_ptr_field_before_getfield(). We hope there is # no getfield_gc in the middle of initialization code, but there # shouldn't be, given that a 'new' is already delayed by previous # optimization steps. In practice it should immediately be # followed by a bunch of 'setfields', and the 'pending_zeros' # optimization we do here is meant for this case. self.emit_pending_zeros() self.emit_gc_load_or_indexed(op, ptr_box, ConstInt(0), itemsize, 1, ofs, sign) self.emit_op(op) return True self.emit_gc_load_or_indexed(op, ptr_box, ConstInt(0), itemsize, 1, ofs, sign) elif opnum in (rop.SETFIELD_GC, rop.SETFIELD_RAW): ofs, itemsize, sign = unpack_fielddescr(op.getdescr()) ptr_box = op.getarg(0) value_box = op.getarg(1) self.emit_gc_store_or_indexed(op, ptr_box, ConstInt(0), value_box, itemsize, 1, ofs) elif opnum == rop.ARRAYLEN_GC: descr = op.getdescr() assert isinstance(descr, ArrayDescr) ofs = descr.lendescr.offset self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD, 1, ofs, NOT_SIGNED) elif opnum == rop.STRLEN: basesize, itemsize, ofs_length = get_array_token( rstr.STR, self.cpu.translate_support_code) self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD, 1, ofs_length, NOT_SIGNED) elif opnum == rop.UNICODELEN: basesize, itemsize, ofs_length = get_array_token( rstr.UNICODE, self.cpu.translate_support_code) self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD, 1, ofs_length, NOT_SIGNED) elif opnum == rop.STRHASH: offset, size = get_field_token(rstr.STR, 'hash', self.cpu.translate_support_code) assert size == WORD self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD, 1, offset, sign=True) elif opnum == rop.UNICODEHASH: offset, size = get_field_token(rstr.UNICODE, 'hash', self.cpu.translate_support_code) assert size == WORD self.emit_gc_load_or_indexed(op, op.getarg(0), ConstInt(0), WORD, 1, offset, sign=True) elif opnum == rop.STRGETITEM: basesize, itemsize, ofs_length = get_array_token( rstr.STR, self.cpu.translate_support_code) assert itemsize == 1 basesize -= 1 # for the extra null character self.emit_gc_load_or_indexed(op, op.getarg(0), op.getarg(1), itemsize, itemsize, basesize, NOT_SIGNED) elif opnum == rop.UNICODEGETITEM: basesize, itemsize, ofs_length = get_array_token( rstr.UNICODE, self.cpu.translate_support_code) self.emit_gc_load_or_indexed(op, op.getarg(0), op.getarg(1), itemsize, itemsize, basesize, NOT_SIGNED) elif opnum == rop.STRSETITEM: basesize, itemsize, ofs_length = get_array_token( rstr.STR, self.cpu.translate_support_code) assert itemsize == 1 basesize -= 1 # for the extra null character self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1), op.getarg(2), itemsize, itemsize, basesize) elif opnum == rop.UNICODESETITEM: basesize, itemsize, ofs_length = get_array_token( rstr.UNICODE, self.cpu.translate_support_code) self.emit_gc_store_or_indexed(op, op.getarg(0), op.getarg(1), op.getarg(2), itemsize, itemsize, basesize) return False def rewrite(self, operations, gcrefs_output_list): # we can only remember one malloc since the next malloc can possibly # collect; but we can try to collapse several known-size mallocs into # one, both for performance and to reduce the number of write # barriers. We do this on each "basic block" of operations, which in # this case means between CALLs or unknown-size mallocs. # self.gcrefs_output_list = gcrefs_output_list self.gcrefs_map = None self.gcrefs_recently_loaded = None operations = self.remove_bridge_exception(operations) self._changed_op = None for i in range(len(operations)): op = operations[i] if op.get_forwarded(): msg = '[rewrite] operations at %d has forwarded info %s\n' % ( i, op.repr({})) if we_are_translated(): llop.debug_print(lltype.Void, msg) raise NotImplementedError(msg) if op.getopnum() == rop.DEBUG_MERGE_POINT: continue if op is self._changed_op: op = self._changed_op_to # ---------- GC_LOAD/STORE transformations -------------- if self.transform_to_gc_load(op): continue # ---------- turn NEWxxx into CALL_MALLOC_xxx ---------- if rop.is_malloc(op.opnum): self.handle_malloc_operation(op) continue if (rop.is_guard(op.opnum) or self.could_merge_with_next_guard(op, i, operations)): self.emit_pending_zeros() elif rop.can_malloc(op.opnum): self.emitting_an_operation_that_can_collect() elif op.getopnum() == rop.LABEL: self.emit_label() # ---------- write barriers ---------- if self.gc_ll_descr.write_barrier_descr is not None: if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) self.handle_write_barrier_setfield(op) continue if op.getopnum() == rop.SETINTERIORFIELD_GC: self.handle_write_barrier_setinteriorfield(op) continue if op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) self.handle_write_barrier_setarrayitem(op) continue else: # this is dead code, but in case we have a gc that does # not have a write barrier and does not zero memory, we would # need to call it if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) elif op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) # ---------- call assembler ----------- if OpHelpers.is_call_assembler(op.getopnum()): self.handle_call_assembler(op) continue if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH: self.emit_pending_zeros() # self.emit_op(op) return self._newops def could_merge_with_next_guard(self, op, i, operations): # return True in cases where the operation and the following guard # should likely remain together. Simplified version of # can_merge_with_next_guard() in llsupport/regalloc.py. if not rop.is_comparison(op.opnum): return rop.is_ovf(op.opnum) # int_xxx_ovf() / guard_no_overflow() if i + 1 >= len(operations): return False next_op = operations[i + 1] opnum = next_op.getopnum() if not (opnum == rop.GUARD_TRUE or opnum == rop.GUARD_FALSE or opnum == rop.COND_CALL): return False if next_op.getarg(0) is not op: return False self.remove_tested_failarg(next_op) return True def remove_tested_failarg(self, op): opnum = op.getopnum() if not (opnum == rop.GUARD_TRUE or opnum == rop.GUARD_FALSE): return if op.getarg(0).is_vector(): return try: i = op.getfailargs().index(op.getarg(0)) except ValueError: return # The checked value is also in the failargs. The front-end # tries not to produce it, but doesn't always succeed (and # it's hard to test all cases). Rewrite it away. value = int(opnum == rop.GUARD_FALSE) op1 = ResOperation(rop.SAME_AS_I, [ConstInt(value)]) self.emit_op(op1) lst = op.getfailargs()[:] lst[i] = op1 newop = op.copy_and_change(opnum) newop.setfailargs(lst) self._changed_op = op self._changed_op_to = newop # ---------- def handle_malloc_operation(self, op): opnum = op.getopnum() if opnum == rop.NEW: self.handle_new_fixedsize(op.getdescr(), op) elif opnum == rop.NEW_WITH_VTABLE: descr = op.getdescr() self.handle_new_fixedsize(descr, op) if self.gc_ll_descr.fielddescr_vtable is not None: self.emit_setfield(op, ConstInt(descr.get_vtable()), descr=self.gc_ll_descr.fielddescr_vtable) elif opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR: descr = op.getdescr() assert isinstance(descr, ArrayDescr) self.handle_new_array(descr, op) elif opnum == rop.NEWSTR: self.handle_new_array(self.gc_ll_descr.str_descr, op, kind=FLAG_STR) elif opnum == rop.NEWUNICODE: self.handle_new_array(self.gc_ll_descr.unicode_descr, op, kind=FLAG_UNICODE) else: raise NotImplementedError(op.getopname()) def clear_gc_fields(self, descr, result): if self.gc_ll_descr.malloc_zero_filled: return d = self.delayed_zero_setfields(result) for fielddescr in descr.gc_fielddescrs: ofs = self.cpu.unpack_fielddescr(fielddescr) d[ofs] = None def consider_setfield_gc(self, op): offset = self.cpu.unpack_fielddescr(op.getdescr()) try: del self._delayed_zero_setfields[self.get_box_replacement( op.getarg(0))][offset] except KeyError: pass def consider_setarrayitem_gc(self, op): array_box = op.getarg(0) index_box = op.getarg(1) if not isinstance(array_box, ConstPtr) and index_box.is_constant(): self.remember_setarrayitem_occured(array_box, index_box.getint()) def clear_varsize_gc_fields(self, kind, descr, result, v_length, opnum): if self.gc_ll_descr.malloc_zero_filled: return if kind == FLAG_ARRAY: if descr.is_array_of_structs() or descr.is_array_of_pointers(): assert opnum == rop.NEW_ARRAY_CLEAR if opnum == rop.NEW_ARRAY_CLEAR: self.handle_clear_array_contents(descr, result, v_length) return if kind == FLAG_STR: hash_descr = self.gc_ll_descr.str_hash_descr elif kind == FLAG_UNICODE: hash_descr = self.gc_ll_descr.unicode_hash_descr else: return self.emit_setfield(result, self.c_zero, descr=hash_descr) def handle_new_fixedsize(self, descr, op): assert isinstance(descr, SizeDescr) size = descr.size if self.gen_malloc_nursery(size, op): self.gen_initialize_tid(op, descr.tid) else: self.gen_malloc_fixedsize(size, descr.tid, op) self.clear_gc_fields(descr, op) def handle_new_array(self, arraydescr, op, kind=FLAG_ARRAY): v_length = self.get_box_replacement(op.getarg(0)) total_size = -1 if isinstance(v_length, ConstInt): num_elem = v_length.getint() self.remember_known_length(op, num_elem) try: var_size = ovfcheck(arraydescr.itemsize * num_elem) total_size = ovfcheck(arraydescr.basesize + var_size) except OverflowError: pass # total_size is still -1 elif arraydescr.itemsize == 0: total_size = arraydescr.basesize elif (self.gc_ll_descr.can_use_nursery_malloc(1) and self.gen_malloc_nursery_varsize( arraydescr.itemsize, v_length, op, arraydescr, kind=kind)): # note that we cannot initialize tid here, because the array # might end up being allocated by malloc_external or some # stuff that initializes GC header fields differently self.gen_initialize_len(op, v_length, arraydescr.lendescr) self.clear_varsize_gc_fields(kind, op.getdescr(), op, v_length, op.getopnum()) return if (total_size >= 0 and self.gen_malloc_nursery(total_size, op)): self.gen_initialize_tid(op, arraydescr.tid) self.gen_initialize_len(op, v_length, arraydescr.lendescr) elif self.gc_ll_descr.kind == 'boehm': self.gen_boehm_malloc_array(arraydescr, v_length, op) else: opnum = op.getopnum() if opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR: self.gen_malloc_array(arraydescr, v_length, op) elif opnum == rop.NEWSTR: self.gen_malloc_str(v_length, op) elif opnum == rop.NEWUNICODE: self.gen_malloc_unicode(v_length, op) else: raise NotImplementedError(op.getopname()) self.clear_varsize_gc_fields(kind, op.getdescr(), op, v_length, op.getopnum()) def handle_clear_array_contents(self, arraydescr, v_arr, v_length): assert v_length is not None if isinstance(v_length, ConstInt) and v_length.getint() == 0: return # the ZERO_ARRAY operation will be optimized according to what # SETARRAYITEM_GC we see before the next allocation operation. # See emit_pending_zeros(). (This optimization is done by # hacking the object 'o' in-place: e.g., o.getarg(1) may be # replaced with another constant greater than 0.) assert isinstance(arraydescr, ArrayDescr) scale = arraydescr.itemsize v_length_scaled = v_length if not isinstance(v_length, ConstInt): scale, offset, v_length_scaled = \ self._emit_mul_if_factor_offset_not_supported(v_length, scale, 0) v_scale = ConstInt(scale) # there is probably no point in doing _emit_mul_if.. for c_zero! # NOTE that the scale might be != 1 for e.g. v_length_scaled if it is a constant # it is later applied in emit_pending_zeros args = [v_arr, self.c_zero, v_length_scaled, ConstInt(scale), v_scale] o = ResOperation(rop.ZERO_ARRAY, args, descr=arraydescr) self.emit_op(o) if isinstance(v_length, ConstInt): self.last_zero_arrays.append(self._newops[-1]) def gen_malloc_frame(self, frame_info): descrs = self.gc_ll_descr.getframedescrs(self.cpu) if self.gc_ll_descr.kind == 'boehm': ofs, size, sign = unpack_fielddescr(descrs.jfi_frame_depth) if sign: size = -size args = [ConstInt(frame_info), ConstInt(ofs), ConstInt(size)] size = ResOperation(rop.GC_LOAD_I, args) self.emit_op(size) frame = ResOperation(rop.NEW_ARRAY, [size], descr=descrs.arraydescr) self.handle_new_array(descrs.arraydescr, frame) return self.get_box_replacement(frame) else: # we read size in bytes here, not the length ofs, size, sign = unpack_fielddescr(descrs.jfi_frame_size) if sign: size = -size args = [ConstInt(frame_info), ConstInt(ofs), ConstInt(size)] size = ResOperation(rop.GC_LOAD_I, args) self.emit_op(size) frame = self.gen_malloc_nursery_varsize_frame(size) self.gen_initialize_tid(frame, descrs.arraydescr.tid) # we need to explicitely zero all the gc fields, because # of the unusal malloc pattern length = self.emit_getfield(ConstInt(frame_info), descr=descrs.jfi_frame_depth, raw=True) self.emit_setfield(frame, self.c_zero, descr=descrs.jf_extra_stack_depth) self.emit_setfield(frame, self.c_null, descr=descrs.jf_savedata) self.emit_setfield(frame, self.c_null, descr=descrs.jf_force_descr) self.emit_setfield(frame, self.c_null, descr=descrs.jf_descr) self.emit_setfield(frame, self.c_null, descr=descrs.jf_guard_exc) self.emit_setfield(frame, self.c_null, descr=descrs.jf_forward) self.gen_initialize_len(frame, length, descrs.arraydescr.lendescr) return self.get_box_replacement(frame) def emit_getfield(self, ptr, descr, type='i', raw=False): ofs, size, sign = unpack_fielddescr(descr) op = self.emit_gc_load_or_indexed(None, ptr, ConstInt(0), size, 1, ofs, sign) return op def emit_setfield(self, ptr, value, descr): ofs, size, sign = unpack_fielddescr(descr) self.emit_gc_store_or_indexed(None, ptr, ConstInt(0), value, size, 1, ofs) def handle_call_assembler(self, op): descrs = self.gc_ll_descr.getframedescrs(self.cpu) loop_token = op.getdescr() assert isinstance(loop_token, history.JitCellToken) jfi = loop_token.compiled_loop_token.frame_info llfi = heaptracker.adr2int(llmemory.cast_ptr_to_adr(jfi)) frame = self.gen_malloc_frame(llfi) self.emit_setfield(frame, history.ConstInt(llfi), descr=descrs.jf_frame_info) arglist = op.getarglist() index_list = loop_token.compiled_loop_token._ll_initial_locs for i, arg in enumerate(arglist): descr = self.cpu.getarraydescr_for_frame(arg.type) assert self.cpu.JITFRAME_FIXED_SIZE & 1 == 0 _, itemsize, _ = self.cpu.unpack_arraydescr_size(descr) array_offset = index_list[i] # index, already measured in bytes # emit GC_STORE _, basesize, _ = unpack_arraydescr(descr) offset = basesize + array_offset args = [frame, ConstInt(offset), arg, ConstInt(itemsize)] self.emit_op(ResOperation(rop.GC_STORE, args)) descr = op.getdescr() assert isinstance(descr, JitCellToken) jd = descr.outermost_jitdriver_sd args = [frame] if jd and jd.index_of_virtualizable >= 0: args = [frame, arglist[jd.index_of_virtualizable]] else: args = [frame] call_asm = ResOperation(op.getopnum(), args, descr=op.getdescr()) self.replace_op_with(self.get_box_replacement(op), call_asm) self.emit_op(call_asm) # ---------- def emitting_an_operation_that_can_collect(self): # must be called whenever we emit an operation that can collect: # forgets the previous MALLOC_NURSERY, if any; and empty the # set 'write_barrier_applied', so that future SETFIELDs will generate # a write barrier as usual. # it also writes down all the pending zero ptr fields self._op_malloc_nursery = None self._write_barrier_applied.clear() self.emit_pending_zeros() def write_barrier_applied(self, op): return self.get_box_replacement(op) in self._write_barrier_applied def remember_write_barrier(self, op): self._write_barrier_applied[self.get_box_replacement(op)] = None def emit_pending_zeros(self): # First, try to rewrite the existing ZERO_ARRAY operations from # the 'last_zero_arrays' list. Note that these operation objects # are also already in 'newops', which is the point. for op in self.last_zero_arrays: assert op.getopnum() == rop.ZERO_ARRAY descr = op.getdescr() assert isinstance(descr, ArrayDescr) scale = descr.itemsize box = op.getarg(0) try: intset = self.setarrayitems_occurred(box) except KeyError: start_box = op.getarg(1) length_box = op.getarg(2) if isinstance(start_box, ConstInt): start = start_box.getint() op.setarg(1, ConstInt(start * scale)) op.setarg(3, ConstInt(1)) if isinstance(length_box, ConstInt): stop = length_box.getint() scaled_len = stop * scale op.setarg(2, ConstInt(scaled_len)) op.setarg(4, ConstInt(1)) continue assert op.getarg(1).getint() == 0 # always 'start=0' initially start = 0 while start in intset: start += 1 op.setarg(1, ConstInt(start * scale)) stop = op.getarg(2).getint() assert start <= stop while stop > start and (stop - 1) in intset: stop -= 1 op.setarg(2, ConstInt((stop - start) * scale)) # ^^ may be ConstInt(0); then the operation becomes a no-op op.setarg(3, ConstInt(1)) # set scale to 1 op.setarg(4, ConstInt(1)) # set scale to 1 del self.last_zero_arrays[:] self._setarrayitems_occurred.clear() # # Then write the NULL-pointer-writing ops that are still pending for v, d in self._delayed_zero_setfields.iteritems(): v = self.get_box_replacement(v) for ofs in d.iterkeys(): self.emit_gc_store_or_indexed(None, v, ConstInt(ofs), ConstInt(0), WORD, 1, 0) self._delayed_zero_setfields.clear() def _gen_call_malloc_gc(self, args, v_result, descr): """Generate a CALL_R/CHECK_MEMORY_ERROR with the given args.""" self.emitting_an_operation_that_can_collect() op = ResOperation(rop.CALL_R, args, descr=descr) self.replace_op_with(v_result, op) self.emit_op(op) self.emit_op(ResOperation(rop.CHECK_MEMORY_ERROR, [op])) # In general, don't add v_result to write_barrier_applied: # v_result might be a large young array. def gen_malloc_fixedsize(self, size, typeid, v_result): """Generate a CALL_R(malloc_fixedsize_fn, ...). Used on Boehm, and on the framework GC for large fixed-size mallocs. (For all I know this latter case never occurs in practice, but better safe than sorry.) """ if self.gc_ll_descr.fielddescr_tid is not None: # framework GC assert (size & (WORD - 1)) == 0, "size not aligned?" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_big_fixedsize') args = [ConstInt(addr), ConstInt(size), ConstInt(typeid)] descr = self.gc_ll_descr.malloc_big_fixedsize_descr else: # Boehm addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_fixedsize') args = [ConstInt(addr), ConstInt(size)] descr = self.gc_ll_descr.malloc_fixedsize_descr self._gen_call_malloc_gc(args, v_result, descr) # mark 'v_result' as freshly malloced, so not needing a write barrier # (this is always true because it's a fixed-size object) self.remember_write_barrier(v_result) def gen_boehm_malloc_array(self, arraydescr, v_num_elem, v_result): """Generate a CALL_R(malloc_array_fn, ...) for Boehm.""" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array') self._gen_call_malloc_gc([ ConstInt(addr), ConstInt(arraydescr.basesize), v_num_elem, ConstInt(arraydescr.itemsize), ConstInt(arraydescr.lendescr.offset) ], v_result, self.gc_ll_descr.malloc_array_descr) def gen_malloc_array(self, arraydescr, v_num_elem, v_result): """Generate a CALL_R(malloc_array_fn, ...) going either to the standard or the nonstandard version of the function.""" # if (arraydescr.basesize == self.gc_ll_descr.standard_array_basesize and arraydescr.lendescr.offset == self.gc_ll_descr.standard_array_length_ofs): # this is a standard-looking array, common case addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array') args = [ ConstInt(addr), ConstInt(arraydescr.itemsize), ConstInt(arraydescr.tid), v_num_elem ] calldescr = self.gc_ll_descr.malloc_array_descr else: # rare case, so don't care too much about the number of arguments addr = self.gc_ll_descr.get_malloc_fn_addr( 'malloc_array_nonstandard') args = [ ConstInt(addr), ConstInt(arraydescr.basesize), ConstInt(arraydescr.itemsize), ConstInt(arraydescr.lendescr.offset), ConstInt(arraydescr.tid), v_num_elem ] calldescr = self.gc_ll_descr.malloc_array_nonstandard_descr self._gen_call_malloc_gc(args, v_result, calldescr) def gen_malloc_str(self, v_num_elem, v_result): """Generate a CALL_R(malloc_str_fn, ...).""" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_str') self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result, self.gc_ll_descr.malloc_str_descr) def gen_malloc_unicode(self, v_num_elem, v_result): """Generate a CALL_R(malloc_unicode_fn, ...).""" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_unicode') self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result, self.gc_ll_descr.malloc_unicode_descr) def gen_malloc_nursery_varsize(self, itemsize, v_length, v_result, arraydescr, kind=FLAG_ARRAY): """ itemsize is an int, v_length and v_result are boxes """ gc_descr = self.gc_ll_descr if (kind == FLAG_ARRAY and (arraydescr.basesize != gc_descr.standard_array_basesize or arraydescr.lendescr.offset != gc_descr.standard_array_length_ofs)): return False self.emitting_an_operation_that_can_collect() op = ResOperation( rop.CALL_MALLOC_NURSERY_VARSIZE, [ConstInt(kind), ConstInt(itemsize), v_length], descr=arraydescr) self.replace_op_with(v_result, op) self.emit_op(op) # don't record v_result into self.write_barrier_applied: # it can be a large, young array with card marking, and then # the GC relies on the write barrier being called return True def gen_malloc_nursery_varsize_frame(self, sizebox): """ Generate CALL_MALLOC_NURSERY_VARSIZE_FRAME """ self.emitting_an_operation_that_can_collect() op = ResOperation(rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME, [sizebox]) self.emit_op(op) self.remember_write_barrier(op) return op def gen_malloc_nursery(self, size, v_result): """Try to generate or update a CALL_MALLOC_NURSERY. If that succeeds, return True; you still need to write the tid. If that fails, return False. """ size = self.round_up_for_allocation(size) if not self.gc_ll_descr.can_use_nursery_malloc(size): return False # op = None if self._op_malloc_nursery is not None: # already a MALLOC_NURSERY: increment its total size total_size = self._op_malloc_nursery.getarg(0).getint() total_size += size if self.gc_ll_descr.can_use_nursery_malloc(total_size): # if the total size is still reasonable, merge it self._op_malloc_nursery.setarg(0, ConstInt(total_size)) op = ResOperation(rop.NURSERY_PTR_INCREMENT, [ self._v_last_malloced_nursery, ConstInt(self._previous_size) ]) self.replace_op_with(v_result, op) if op is None: # if we failed to merge with a previous MALLOC_NURSERY, emit one self.emitting_an_operation_that_can_collect() op = ResOperation(rop.CALL_MALLOC_NURSERY, [ConstInt(size)]) self.replace_op_with(v_result, op) self._op_malloc_nursery = op # self.emit_op(op) self._previous_size = size self._v_last_malloced_nursery = op self.remember_write_barrier(op) return True def gen_initialize_tid(self, v_newgcobj, tid): if self.gc_ll_descr.fielddescr_tid is not None: # produce a SETFIELD to initialize the GC header self.emit_setfield(v_newgcobj, ConstInt(tid), descr=self.gc_ll_descr.fielddescr_tid) def gen_initialize_len(self, v_newgcobj, v_length, arraylen_descr): # produce a SETFIELD to initialize the array length self.emit_setfield(v_newgcobj, v_length, descr=arraylen_descr) # ---------- def handle_write_barrier_setfield(self, op): val = op.getarg(0) if not self.write_barrier_applied(val): v = op.getarg(1) if (v.type == 'r' and (not isinstance(v, ConstPtr) or rgc.needs_write_barrier(v.value))): self.gen_write_barrier(val) #op = op.copy_and_change(rop.SETFIELD_RAW) self.emit_op(op) def handle_write_barrier_setarrayitem(self, op): val = op.getarg(0) if not self.write_barrier_applied(val): v = op.getarg(2) if (v.type == 'r' and (not isinstance(v, ConstPtr) or rgc.needs_write_barrier(v.value))): self.gen_write_barrier_array(val, op.getarg(1)) #op = op.copy_and_change(rop.SET{ARRAYITEM,INTERIORFIELD}_RAW) self.emit_op(op) handle_write_barrier_setinteriorfield = handle_write_barrier_setarrayitem def gen_write_barrier(self, v_base): write_barrier_descr = self.gc_ll_descr.write_barrier_descr args = [v_base] self.emit_op( ResOperation(rop.COND_CALL_GC_WB, args, descr=write_barrier_descr)) self.remember_write_barrier(v_base) def gen_write_barrier_array(self, v_base, v_index): write_barrier_descr = self.gc_ll_descr.write_barrier_descr if write_barrier_descr.has_write_barrier_from_array(self.cpu): # If we know statically the length of 'v', and it is not too # big, then produce a regular write_barrier. If it's unknown or # too big, produce instead a write_barrier_from_array. LARGE = 130 length = self.known_length(v_base, LARGE) if length >= LARGE: # unknown or too big: produce a write_barrier_from_array args = [v_base, v_index] self.emit_op( ResOperation(rop.COND_CALL_GC_WB_ARRAY, args, descr=write_barrier_descr)) # a WB_ARRAY is not enough to prevent any future write # barriers, so don't add to 'write_barrier_applied'! return # fall-back case: produce a write_barrier self.gen_write_barrier(v_base) def round_up_for_allocation(self, size): if not self.gc_ll_descr.round_up: return size if self.gc_ll_descr.translate_support_code: from rpython.rtyper.lltypesystem import llarena return llarena.round_up_for_allocation( size, self.gc_ll_descr.minimal_size_in_nursery) else: # non-translated: do it manually # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs size = max(size, 2 * WORD) return (size + WORD - 1) & ~(WORD - 1) # round up def remove_bridge_exception(self, operations): """Check a common case: 'save_exception' immediately followed by 'restore_exception' at the start of the bridge.""" # XXX should check if the boxes are used later; but we just assume # they aren't for now start = 0 if operations[0].getopnum() == rop.INCREMENT_DEBUG_COUNTER: start = 1 if len(operations) >= start + 3: if (operations[start + 0].getopnum() == rop.SAVE_EXC_CLASS and operations[start + 1].getopnum() == rop.SAVE_EXCEPTION and operations[start + 2].getopnum() == rop.RESTORE_EXCEPTION): return operations[:start] + operations[start + 3:] return operations def emit_label(self): self.emitting_an_operation_that_can_collect() self._known_lengths.clear() self.gcrefs_recently_loaded = None def _gcref_index(self, gcref): if self.gcrefs_map is None: self.gcrefs_map = r_dict(rd_eq, rd_hash) try: return self.gcrefs_map[gcref] except KeyError: pass index = len(self.gcrefs_output_list) self.gcrefs_map[gcref] = index self.gcrefs_output_list.append(gcref) return index def remove_constptr(self, c): """Remove all ConstPtrs, and replace them with load_from_gc_table. """ # Note: currently, gcrefs_recently_loaded is only cleared in # LABELs. We'd like something better, like "don't spill it", # but that's the wrong level... index = self._gcref_index(c.value) if self.gcrefs_recently_loaded is None: self.gcrefs_recently_loaded = {} try: load_op = self.gcrefs_recently_loaded[index] except KeyError: load_op = ResOperation(rop.LOAD_FROM_GC_TABLE, [ConstInt(index)]) self._newops.append(load_op) self.gcrefs_recently_loaded[index] = load_op return load_op
def test_constant_in_call_malloc(self): c = ConstPtr(rffi.cast(llmemory.GCREF, 0xdeadbeef1234)) self.ensure_can_hold(rop.COND_CALL, [c], descr=self.calldescr) assert self.const_in_pool(c) assert self.const_in_pool( ConstPtr(rffi.cast(llmemory.GCREF, 0xdeadbeef1234)))
def test_bug_2(): cpu = CPU(None, None) cpu.setup_once() S4 = lltype.Struct('Sx', ("f0", lltype.Char), ("f1", lltype.Signed), ("f2", lltype.Signed), ("f3", lltype.Signed)) S5 = lltype.GcArray(S4) v1 = BoxInt() v2 = BoxInt() v3 = BoxInt() v4 = BoxInt() v5 = BoxInt() v6 = BoxInt() v7 = BoxInt() v8 = BoxInt() v9 = BoxInt() v10 = BoxInt() tmp11 = BoxInt() tmp12 = BoxPtr() faildescr0 = BasicFailDescr() tmp13 = BoxPtr() faildescr1 = BasicFailDescr() finishdescr2 = BasicFinalDescr() const_ptr14 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 1))) const_ptr15 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 489))) const_ptr16 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 16))) const_ptr17 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S5, 299))) inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10] xtp, func, funcdescr = getexception(cpu, 3) xtp2, func2, func2descr = getexception(cpu, 2) operations = [ ResOperation(rop.STRGETITEM, [const_ptr14, ConstInt(0)], tmp11), ResOperation(rop.LABEL, [v1, v2, tmp11, v3, v4, v5, v6, v7, v8, v9, v10], None, TargetToken()), ResOperation(rop.UNICODESETITEM, [const_ptr15, v4, ConstInt(22)], None), ResOperation(rop.CALL, [ConstInt(func), v2, v1, v9], None, descr=funcdescr), ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp)], tmp12, descr=faildescr0), ResOperation( rop.UNICODESETITEM, [const_ptr16, ConstInt(13), ConstInt(9)], None), ResOperation(rop.SETINTERIORFIELD_GC, [const_ptr17, v3, v7], None, cpu.interiorfielddescrof(S5, 'f3')), ResOperation(rop.CALL, [ConstInt(func2), v7, v10], None, descr=func2descr), ResOperation(rop.GUARD_NO_EXCEPTION, [], tmp13, descr=faildescr1), ResOperation(rop.FINISH, [], None, descr=finishdescr2), ] operations[4].setfailargs([v4, v8, v10, v2, v9, v7, v6, v1]) operations[8].setfailargs([v3, v9, v2, v6, v4]) looptoken = JitCellToken() cpu.compile_loop(inputargs, operations, looptoken) loop_args = [1, -39, 46, 21, 16, 6, -4611686018427387905, 12, 14, 2] frame = cpu.execute_token(looptoken, *loop_args) assert cpu.get_int_value(frame, 0) == 46 assert cpu.get_int_value(frame, 1) == 14 assert cpu.get_int_value(frame, 2) == -39 assert cpu.get_int_value(frame, 3) == 6 assert cpu.get_int_value(frame, 4) == 21 S4 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT), ("f0", lltype.Signed)) S5 = lltype.GcStruct('Sx', ("f0", lltype.Signed)) S6 = lltype.GcArray(lltype.Signed) S7 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT), ("f0", lltype.Char)) S8 = lltype.Struct('Sx', ("f0", lltype.Char), ("f1", lltype.Signed), ("f2", lltype.Signed), ("f3", lltype.Signed)) S9 = lltype.GcArray(S8) v1 = BoxInt() v2 = BoxInt() v3 = BoxInt() v4 = BoxInt() v5 = BoxInt() v6 = BoxInt() v7 = BoxInt() v8 = BoxInt() v9 = BoxInt() v10 = BoxInt() v11 = BoxInt() v12 = BoxInt() v13 = BoxInt() v14 = BoxInt() v15 = BoxInt() v16 = BoxInt() v17 = BoxInt() v18 = BoxInt() v19 = BoxInt() p20 = BoxPtr() tmp21 = BoxPtr() faildescr3 = BasicFailDescr() tmp22 = BoxPtr() faildescr4 = BasicFailDescr() tmp23 = BoxInt() tmp24 = BoxInt() tmp25 = BoxInt() tmp26 = BoxInt() tmp27 = BoxInt() tmp28 = BoxInt() tmp29 = BoxInt() faildescr5 = BasicFailDescr() tmp30 = BoxPtr() faildescr6 = BasicFailDescr() finishdescr7 = BasicFinalDescr() const_ptr31 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S4))) const_ptr32 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 46))) const_ptr33 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S5))) const_ptr34 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 26))) const_ptr35 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 15))) const_ptr36 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S7))) const_ptr37 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 484))) const_ptr38 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S9, 299))) inputargs = [v1, v2, v3, v4, v5] func3, func3descr = getnoexception(cpu, 5) xtp3, func4, func4descr = getexception(cpu, 10) operations = [ ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp2)], tmp21, descr=faildescr3), ResOperation(rop.INT_IS_ZERO, [v4], v6), ResOperation(rop.INT_NE, [v6, ConstInt(13)], v7), ResOperation(rop.GETFIELD_GC, [const_ptr31], v8, cpu.fielddescrof(S4, 'f0')), ResOperation(rop.STRSETITEM, [const_ptr32, v6, ConstInt(0)], None), ResOperation(rop.NEWSTR, [ConstInt(5)], tmp22), ResOperation(rop.STRSETITEM, [tmp22, ConstInt(0), ConstInt(42)], None), ResOperation(rop.STRSETITEM, [tmp22, ConstInt(1), ConstInt(42)], None), ResOperation(rop.STRSETITEM, [tmp22, ConstInt(2), ConstInt(20)], None), ResOperation(rop.STRSETITEM, [tmp22, ConstInt(3), ConstInt(48)], None), ResOperation(rop.STRSETITEM, [tmp22, ConstInt(4), ConstInt(6)], None), ResOperation(rop.GETFIELD_GC, [const_ptr33], v9, cpu.fielddescrof(S5, 'f0')), ResOperation(rop.UNICODESETITEM, [const_ptr34, ConstInt(24), ConstInt(65533)], None), ResOperation(rop.GETFIELD_GC, [const_ptr31], v10, cpu.fielddescrof(S4, 'f0')), ResOperation(rop.INT_NE, [v10, ConstInt(25)], v11), ResOperation(rop.CALL, [ConstInt(func3), v5, v1, v8, v3, v2], v12, descr=func3descr), ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=faildescr4), ResOperation(rop.UNICODELEN, [const_ptr35], tmp23), ResOperation(rop.NEW_ARRAY, [v2], p20, cpu.arraydescrof(S6)), ResOperation(rop.GETFIELD_GC, [const_ptr36], v13, cpu.fielddescrof(S7, 'f0')), ResOperation(rop.INT_OR, [v8, ConstInt(2)], tmp24), ResOperation(rop.INT_FLOORDIV, [ConstInt(8), tmp24], v14), ResOperation(rop.GETARRAYITEM_GC, [p20, ConstInt(3)], v15, cpu.arraydescrof(S6)), ResOperation( rop.COPYSTRCONTENT, [tmp22, const_ptr37, ConstInt(1), ConstInt(163), ConstInt(0)], None), ResOperation(rop.COPYUNICODECONTENT, [const_ptr35, const_ptr34, ConstInt(13), ConstInt(0), v6], None), ResOperation(rop.STRGETITEM, [tmp22, v6], tmp25), ResOperation(rop.STRGETITEM, [tmp22, ConstInt(0)], tmp26), ResOperation(rop.GETINTERIORFIELD_GC, [const_ptr38, v13], v16, cpu.interiorfielddescrof(S9, 'f0')), ResOperation(rop.INT_GE, [v4, v5], v17), ResOperation(rop.INT_OR, [v13, ConstInt(2)], tmp27), ResOperation(rop.INT_FLOORDIV, [ConstInt(12), tmp27], v18), ResOperation(rop.INT_AND, [v1, ConstInt(-4)], tmp28), ResOperation(rop.INT_OR, [tmp28, ConstInt(2)], tmp29), ResOperation(rop.INT_FLOORDIV, [v15, tmp29], v19), ResOperation(rop.GUARD_FALSE, [v17], None, descr=faildescr5), ResOperation(rop.UNICODESETITEM, [const_ptr34, ConstInt(20), ConstInt(65522)], None), ResOperation( rop.CALL, [ConstInt(func4), v3, v9, v10, v8, v11, v5, v13, v14, v15, v6], None, descr=func4descr), ResOperation(rop.GUARD_NO_EXCEPTION, [], tmp30, descr=faildescr6), ResOperation(rop.FINISH, [], None, descr=finishdescr7), ] operations[0].setfailargs([]) operations[16].setfailargs([v5, v9]) operations[34].setfailargs([]) operations[37].setfailargs([v12, v19, v10, v7, v4, v8, v18, v15, v9]) cpu.compile_bridge(faildescr1, inputargs, operations, looptoken) frame = cpu.execute_token(looptoken, *loop_args) #assert cpu.get_int_value(frame, 0) == -9223372036854775766 assert cpu.get_int_value(frame, 1) == 0 #assert cpu.get_int_value(frame, 2) == -9223372036854775808 assert cpu.get_int_value(frame, 3) == 1 assert cpu.get_int_value(frame, 4) == 6 #assert cpu.get_int_value(frame, 5) == -9223372036854775808 assert cpu.get_int_value(frame, 6) == 0 assert cpu.get_int_value(frame, 7) == 0 #assert cpu.get_int_value(frame, 8) == 26 S4 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT), ("f0", lltype.Signed), ("f1", lltype.Signed)) S5 = lltype.GcStruct('Sx', ("parent", rclass.OBJECT), ("f0", lltype.Signed)) S6 = lltype.GcStruct('Sx', ("f0", lltype.Signed), ("f1", rffi.UCHAR)) v1 = BoxInt() v2 = BoxInt() v3 = BoxInt() v4 = BoxInt() v5 = BoxInt() v6 = BoxInt() v7 = BoxInt() v8 = BoxInt() v9 = BoxInt() v10 = BoxInt() v11 = BoxInt() v12 = BoxInt() v13 = BoxInt() v14 = BoxInt() v15 = BoxInt() v16 = BoxInt() v17 = BoxInt() v18 = BoxInt() tmp19 = BoxPtr() faildescr8 = BasicFailDescr() tmp20 = BoxInt() tmp21 = BoxInt() tmp22 = BoxInt() tmp23 = BoxInt() faildescr9 = BasicFailDescr() tmp24 = BoxInt() tmp25 = BoxInt() tmp26 = BoxInt() tmp27 = BoxPtr() tmp28 = BoxPtr() faildescr10 = BasicFailDescr() finishdescr11 = BasicFinalDescr() const_ptr29 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S4))) const_ptr30 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 26))) const_ptr31 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.UNICODE, 1))) const_ptr32 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S5))) const_ptr33 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(S6))) const_ptr34 = ConstPtr( lltype.cast_opaque_ptr(llmemory.GCREF, lltype.malloc(rstr.STR, 26))) inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9] operations = [ ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp3)], tmp19, descr=faildescr8), ResOperation(rop.SETFIELD_GC, [const_ptr29, v7], None, cpu.fielddescrof(S4, 'f0')), ResOperation(rop.UNICODEGETITEM, [const_ptr30, ConstInt(21)], tmp20), ResOperation(rop.UNICODEGETITEM, [const_ptr30, ConstInt(10)], tmp21), ResOperation(rop.UINT_RSHIFT, [v9, ConstInt(40)], v10), ResOperation(rop.UNICODEGETITEM, [const_ptr30, ConstInt(25)], tmp22), ResOperation(rop.INT_NE, [ConstInt(-8), v9], v11), ResOperation(rop.INT_MUL_OVF, [v3, ConstInt(-4)], tmp23), ResOperation(rop.GUARD_OVERFLOW, [], None, descr=faildescr9), ResOperation(rop.UNICODESETITEM, [const_ptr31, ConstInt(0), ConstInt(50175)], None), ResOperation(rop.UINT_GT, [v8, ConstInt(-6)], v12), ResOperation(rop.GETFIELD_GC, [const_ptr32], v13, cpu.fielddescrof(S5, 'f0')), ResOperation(rop.INT_AND, [ConstInt(8), v8], v14), ResOperation(rop.INT_INVERT, [v1], v15), ResOperation(rop.SETFIELD_GC, [const_ptr33, ConstInt(3)], None, cpu.fielddescrof(S6, 'f1')), ResOperation(rop.INT_GE, [v14, v6], v16), ResOperation(rop.INT_AND, [v5, ConstInt(-4)], tmp24), ResOperation(rop.INT_OR, [tmp24, ConstInt(2)], tmp25), ResOperation(rop.INT_FLOORDIV, [v9, tmp25], v17), ResOperation(rop.STRLEN, [const_ptr34], tmp26), ResOperation(rop.NEWSTR, [ConstInt(7)], tmp27), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(0), ConstInt(21)], None), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(1), ConstInt(79)], None), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(2), ConstInt(7)], None), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(3), ConstInt(2)], None), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(4), ConstInt(229)], None), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(5), ConstInt(233)], None), ResOperation(rop.STRSETITEM, [tmp27, ConstInt(6), ConstInt(208)], None), ResOperation(rop.INT_LT, [ConstInt(-31), v10], v18), ResOperation(rop.SAME_AS, [ConstPtr(lltype.nullptr(llmemory.GCREF.TO))], tmp28), ResOperation(rop.GUARD_NONNULL_CLASS, [tmp28, ConstInt(xtp2)], None, descr=faildescr10), ResOperation(rop.FINISH, [v4], None, descr=finishdescr11), ] operations[0].setfailargs([]) operations[8].setfailargs([tmp23, v5, v3, v11, v6]) operations[30].setfailargs([v6]) cpu.compile_bridge(faildescr6, inputargs, operations, looptoken) frame = cpu.execute_token(looptoken, *loop_args) #assert cpu.get_int_value(frame, 0) == -9223372036854775808 v1 = BoxInt() v2 = BoxInt() p3 = BoxPtr() tmp4 = BoxInt() tmp5 = BoxPtr() faildescr12 = BasicFailDescr() finishdescr13 = BasicFinalDescr() inputargs = [v1] _, func5, func5descr = getexception(cpu, 0) vt = getvtable(cpu, S4) operations = [ ResOperation(rop.INT_AND, [v1, ConstInt(63)], tmp4), ResOperation(rop.INT_LSHIFT, [ConstInt(10), tmp4], v2), ResOperation(rop.NEW_WITH_VTABLE, [ConstInt(vt)], p3), ResOperation(rop.CALL, [ConstInt(func5)], None, descr=func5descr), ResOperation(rop.GUARD_EXCEPTION, [ConstInt(xtp2)], tmp5, descr=faildescr12), ResOperation(rop.FINISH, [], None, descr=finishdescr13), ] operations[4].setfailargs([v2]) cpu.compile_bridge(faildescr10, inputargs, operations, looptoken) frame = cpu.execute_token(looptoken, *loop_args)
class GcRewriterAssembler(object): """ This class performs the following rewrites on the list of operations: - Turn all NEW_xxx to either a CALL_MALLOC_GC, or a CALL_MALLOC_NURSERY followed by SETFIELDs in order to initialize their GC fields. The two advantages of CALL_MALLOC_NURSERY is that it inlines the common path, and we need only one such operation to allocate several blocks of memory at once. - Add COND_CALLs to the write barrier before SETFIELD_GC and SETARRAYITEM_GC operations. 'write_barrier_applied' contains a dictionary of variable -> None. If a variable is in the dictionary, next setfields can be called without a write barrier. The idea is that an object that was freshly allocated or already write_barrier'd don't need another write_barrier if there was no potentially collecting resop inbetween. """ _previous_size = -1 _op_malloc_nursery = None _v_last_malloced_nursery = None c_zero = ConstInt(0) c_null = ConstPtr(lltype.nullptr(llmemory.GCREF.TO)) def __init__(self, gc_ll_descr, cpu): self.gc_ll_descr = gc_ll_descr self.cpu = cpu self.newops = [] self.known_lengths = {} self.write_barrier_applied = {} self.delayed_zero_setfields = {} self.last_zero_arrays = [] self.setarrayitems_occurred = {} # {box: {set-of-indexes}} def rewrite(self, operations): # we can only remember one malloc since the next malloc can possibly # collect; but we can try to collapse several known-size mallocs into # one, both for performance and to reduce the number of write # barriers. We do this on each "basic block" of operations, which in # this case means between CALLs or unknown-size mallocs. # for i in range(len(operations)): op = operations[i] if op.getopnum() == rop.DEBUG_MERGE_POINT: continue # ---------- GETFIELD_GC ---------- if op.getopnum() == rop.GETFIELD_GC: self.handle_getfield_gc(op) continue # ---------- turn NEWxxx into CALL_MALLOC_xxx ---------- if op.is_malloc(): self.handle_malloc_operation(op) continue if (op.is_guard() or self.could_merge_with_next_guard(op, i, operations)): self.emit_pending_zeros() elif op.can_malloc(): self.emitting_an_operation_that_can_collect() elif op.getopnum() == rop.DEBUG_MERGE_POINT: continue # ignore debug_merge_points elif op.getopnum() == rop.LABEL: self.emitting_an_operation_that_can_collect() self.known_lengths.clear() # ---------- write barriers ---------- if self.gc_ll_descr.write_barrier_descr is not None: if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) self.handle_write_barrier_setfield(op) continue if op.getopnum() == rop.SETINTERIORFIELD_GC: self.handle_write_barrier_setinteriorfield(op) continue if op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) self.handle_write_barrier_setarrayitem(op) continue else: # this is dead code, but in case we have a gc that does # not have a write barrier and does not zero memory, we would # need to clal it if op.getopnum() == rop.SETFIELD_GC: self.consider_setfield_gc(op) elif op.getopnum() == rop.SETARRAYITEM_GC: self.consider_setarrayitem_gc(op) # ---------- call assembler ----------- if op.getopnum() == rop.CALL_ASSEMBLER: self.handle_call_assembler(op) continue if op.getopnum() == rop.JUMP or op.getopnum() == rop.FINISH: self.emit_pending_zeros() # self.newops.append(op) return self.newops def could_merge_with_next_guard(self, op, i, operations): # return True in cases where the operation and the following guard # should likely remain together. Simplified version of # can_merge_with_next_guard() in llsupport/regalloc.py. if not op.is_comparison(): return op.is_ovf() # int_xxx_ovf() / guard_no_overflow() if i + 1 >= len(operations): return False if (operations[i + 1].getopnum() != rop.GUARD_TRUE and operations[i + 1].getopnum() != rop.GUARD_FALSE): return False if operations[i + 1].getarg(0) is not op.result: return False return True # ---------- def handle_getfield_gc(self, op): """See test_zero_ptr_field_before_getfield(). We hope there is no getfield_gc in the middle of initialization code, but there shouldn't be, given that a 'new' is already delayed by previous optimization steps. In practice it should immediately be followed by a bunch of 'setfields', and the 'pending_zeros' optimization we do here is meant for this case.""" self.emit_pending_zeros() self.newops.append(op) # ---------- def handle_malloc_operation(self, op): opnum = op.getopnum() if opnum == rop.NEW: self.handle_new_fixedsize(op.getdescr(), op) elif opnum == rop.NEW_WITH_VTABLE: classint = op.getarg(0).getint() descr = heaptracker.vtable2descr(self.cpu, classint) self.handle_new_fixedsize(descr, op) if self.gc_ll_descr.fielddescr_vtable is not None: op = ResOperation(rop.SETFIELD_GC, [op.result, ConstInt(classint)], None, descr=self.gc_ll_descr.fielddescr_vtable) self.newops.append(op) elif opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR: descr = op.getdescr() assert isinstance(descr, ArrayDescr) self.handle_new_array(descr, op) elif opnum == rop.NEWSTR: self.handle_new_array(self.gc_ll_descr.str_descr, op, kind=FLAG_STR) elif opnum == rop.NEWUNICODE: self.handle_new_array(self.gc_ll_descr.unicode_descr, op, kind=FLAG_UNICODE) else: raise NotImplementedError(op.getopname()) def clear_gc_fields(self, descr, result): if self.gc_ll_descr.malloc_zero_filled: return try: d = self.delayed_zero_setfields[result] except KeyError: d = {} self.delayed_zero_setfields[result] = d for fielddescr in descr.gc_fielddescrs: ofs = self.cpu.unpack_fielddescr(fielddescr) d[ofs] = None def consider_setfield_gc(self, op): offset = self.cpu.unpack_fielddescr(op.getdescr()) try: del self.delayed_zero_setfields[op.getarg(0)][offset] except KeyError: pass def consider_setarrayitem_gc(self, op): array_box = op.getarg(0) index_box = op.getarg(1) if isinstance(array_box, BoxPtr) and isinstance(index_box, ConstInt): try: intset = self.setarrayitems_occurred[array_box] except KeyError: intset = self.setarrayitems_occurred[array_box] = {} intset[index_box.getint()] = None def clear_varsize_gc_fields(self, kind, descr, result, v_length, opnum): if self.gc_ll_descr.malloc_zero_filled: return if kind == FLAG_ARRAY: if descr.is_array_of_structs() or descr.is_array_of_pointers(): assert opnum == rop.NEW_ARRAY_CLEAR if opnum == rop.NEW_ARRAY_CLEAR: self.handle_clear_array_contents(descr, result, v_length) return if kind == FLAG_STR: hash_descr = self.gc_ll_descr.str_hash_descr elif kind == FLAG_UNICODE: hash_descr = self.gc_ll_descr.unicode_hash_descr else: return op = ResOperation(rop.SETFIELD_GC, [result, self.c_zero], None, descr=hash_descr) self.newops.append(op) def handle_new_fixedsize(self, descr, op): assert isinstance(descr, SizeDescr) size = descr.size if self.gen_malloc_nursery(size, op.result): self.gen_initialize_tid(op.result, descr.tid) else: self.gen_malloc_fixedsize(size, descr.tid, op.result) self.clear_gc_fields(descr, op.result) def handle_new_array(self, arraydescr, op, kind=FLAG_ARRAY): v_length = op.getarg(0) total_size = -1 if isinstance(v_length, ConstInt): num_elem = v_length.getint() self.known_lengths[op.result] = num_elem try: var_size = ovfcheck(arraydescr.itemsize * num_elem) total_size = ovfcheck(arraydescr.basesize + var_size) except OverflowError: pass # total_size is still -1 elif arraydescr.itemsize == 0: total_size = arraydescr.basesize elif (self.gc_ll_descr.can_use_nursery_malloc(1) and self.gen_malloc_nursery_varsize(arraydescr.itemsize, v_length, op.result, arraydescr, kind=kind)): # note that we cannot initialize tid here, because the array # might end up being allocated by malloc_external or some # stuff that initializes GC header fields differently self.gen_initialize_len(op.result, v_length, arraydescr.lendescr) self.clear_varsize_gc_fields(kind, op.getdescr(), op.result, v_length, op.getopnum()) return if (total_size >= 0 and self.gen_malloc_nursery(total_size, op.result)): self.gen_initialize_tid(op.result, arraydescr.tid) self.gen_initialize_len(op.result, v_length, arraydescr.lendescr) elif self.gc_ll_descr.kind == 'boehm': self.gen_boehm_malloc_array(arraydescr, v_length, op.result) else: opnum = op.getopnum() if opnum == rop.NEW_ARRAY or opnum == rop.NEW_ARRAY_CLEAR: self.gen_malloc_array(arraydescr, v_length, op.result) elif opnum == rop.NEWSTR: self.gen_malloc_str(v_length, op.result) elif opnum == rop.NEWUNICODE: self.gen_malloc_unicode(v_length, op.result) else: raise NotImplementedError(op.getopname()) self.clear_varsize_gc_fields(kind, op.getdescr(), op.result, v_length, op.getopnum()) def handle_clear_array_contents(self, arraydescr, v_arr, v_length): assert v_length is not None if isinstance(v_length, ConstInt) and v_length.getint() == 0: return # the ZERO_ARRAY operation will be optimized according to what # SETARRAYITEM_GC we see before the next allocation operation. # See emit_pending_zeros(). o = ResOperation(rop.ZERO_ARRAY, [v_arr, self.c_zero, v_length], None, descr=arraydescr) self.newops.append(o) if isinstance(v_length, ConstInt): self.last_zero_arrays.append(o) def gen_malloc_frame(self, frame_info, frame, size_box): descrs = self.gc_ll_descr.getframedescrs(self.cpu) if self.gc_ll_descr.kind == 'boehm': op0 = ResOperation(rop.GETFIELD_RAW, [history.ConstInt(frame_info)], size_box, descr=descrs.jfi_frame_depth) self.newops.append(op0) op1 = ResOperation(rop.NEW_ARRAY, [size_box], frame, descr=descrs.arraydescr) self.handle_new_array(descrs.arraydescr, op1) else: # we read size in bytes here, not the length op0 = ResOperation(rop.GETFIELD_RAW, [history.ConstInt(frame_info)], size_box, descr=descrs.jfi_frame_size) self.newops.append(op0) self.gen_malloc_nursery_varsize_frame(size_box, frame) self.gen_initialize_tid(frame, descrs.arraydescr.tid) length_box = history.BoxInt() # we need to explicitely zero all the gc fields, because # of the unusal malloc pattern extra_ops = [ ResOperation(rop.GETFIELD_RAW, [history.ConstInt(frame_info)], length_box, descr=descrs.jfi_frame_depth), ResOperation(rop.SETFIELD_GC, [frame, self.c_zero], None, descr=descrs.jf_extra_stack_depth), ResOperation(rop.SETFIELD_GC, [frame, self.c_null], None, descr=descrs.jf_savedata), ResOperation(rop.SETFIELD_GC, [frame, self.c_null], None, descr=descrs.jf_force_descr), ResOperation(rop.SETFIELD_GC, [frame, self.c_null], None, descr=descrs.jf_descr), ResOperation(rop.SETFIELD_GC, [frame, self.c_null], None, descr=descrs.jf_guard_exc), ResOperation(rop.SETFIELD_GC, [frame, self.c_null], None, descr=descrs.jf_forward), ] self.newops += extra_ops self.gen_initialize_len(frame, length_box, descrs.arraydescr.lendescr) def handle_call_assembler(self, op): descrs = self.gc_ll_descr.getframedescrs(self.cpu) loop_token = op.getdescr() assert isinstance(loop_token, history.JitCellToken) jfi = loop_token.compiled_loop_token.frame_info llfi = heaptracker.adr2int(llmemory.cast_ptr_to_adr(jfi)) size_box = history.BoxInt() frame = history.BoxPtr() self.gen_malloc_frame(llfi, frame, size_box) op2 = ResOperation(rop.SETFIELD_GC, [frame, history.ConstInt(llfi)], None, descr=descrs.jf_frame_info) self.newops.append(op2) arglist = op.getarglist() index_list = loop_token.compiled_loop_token._ll_initial_locs for i, arg in enumerate(arglist): descr = self.cpu.getarraydescr_for_frame(arg.type) assert self.cpu.JITFRAME_FIXED_SIZE & 1 == 0 _, itemsize, _ = self.cpu.unpack_arraydescr_size(descr) index = index_list[i] // itemsize # index is in bytes self.newops.append( ResOperation(rop.SETARRAYITEM_GC, [frame, ConstInt(index), arg], None, descr)) descr = op.getdescr() assert isinstance(descr, JitCellToken) jd = descr.outermost_jitdriver_sd args = [frame] if jd and jd.index_of_virtualizable >= 0: args = [frame, arglist[jd.index_of_virtualizable]] else: args = [frame] self.newops.append( ResOperation(rop.CALL_ASSEMBLER, args, op.result, op.getdescr())) # ---------- def emitting_an_operation_that_can_collect(self): # must be called whenever we emit an operation that can collect: # forgets the previous MALLOC_NURSERY, if any; and empty the # set 'write_barrier_applied', so that future SETFIELDs will generate # a write barrier as usual. # it also writes down all the pending zero ptr fields self._op_malloc_nursery = None self.write_barrier_applied.clear() self.emit_pending_zeros() def emit_pending_zeros(self): # First, try to rewrite the existing ZERO_ARRAY operations from # the 'last_zero_arrays' list. Note that these operation objects # are also already in 'newops', which is the point. for op in self.last_zero_arrays: assert op.getopnum() == rop.ZERO_ARRAY box = op.getarg(0) try: intset = self.setarrayitems_occurred[box] except KeyError: continue assert op.getarg(1).getint() == 0 # always 'start=0' initially start = 0 while start in intset: start += 1 op.setarg(1, ConstInt(start)) stop = op.getarg(2).getint() assert start <= stop while stop > start and (stop - 1) in intset: stop -= 1 op.setarg(2, ConstInt(stop - start)) # ^^ may be ConstInt(0); then the operation becomes a no-op del self.last_zero_arrays[:] self.setarrayitems_occurred.clear() # # Then write the ZERO_PTR_FIELDs that are still pending for v, d in self.delayed_zero_setfields.iteritems(): for ofs in d.iterkeys(): op = ResOperation(rop.ZERO_PTR_FIELD, [v, ConstInt(ofs)], None) self.newops.append(op) self.delayed_zero_setfields.clear() def _gen_call_malloc_gc(self, args, v_result, descr): """Generate a CALL_MALLOC_GC with the given args.""" self.emitting_an_operation_that_can_collect() op = ResOperation(rop.CALL_MALLOC_GC, args, v_result, descr) self.newops.append(op) # In general, don't add v_result to write_barrier_applied: # v_result might be a large young array. def gen_malloc_fixedsize(self, size, typeid, v_result): """Generate a CALL_MALLOC_GC(malloc_fixedsize_fn, ...). Used on Boehm, and on the framework GC for large fixed-size mallocs. (For all I know this latter case never occurs in practice, but better safe than sorry.) """ if self.gc_ll_descr.fielddescr_tid is not None: # framework GC assert (size & (WORD - 1)) == 0, "size not aligned?" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_big_fixedsize') args = [ConstInt(addr), ConstInt(size), ConstInt(typeid)] descr = self.gc_ll_descr.malloc_big_fixedsize_descr else: # Boehm addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_fixedsize') args = [ConstInt(addr), ConstInt(size)] descr = self.gc_ll_descr.malloc_fixedsize_descr self._gen_call_malloc_gc(args, v_result, descr) # mark 'v_result' as freshly malloced, so not needing a write barrier # (this is always true because it's a fixed-size object) self.write_barrier_applied[v_result] = None def gen_boehm_malloc_array(self, arraydescr, v_num_elem, v_result): """Generate a CALL_MALLOC_GC(malloc_array_fn, ...) for Boehm.""" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array') self._gen_call_malloc_gc([ ConstInt(addr), ConstInt(arraydescr.basesize), v_num_elem, ConstInt(arraydescr.itemsize), ConstInt(arraydescr.lendescr.offset) ], v_result, self.gc_ll_descr.malloc_array_descr) def gen_malloc_array(self, arraydescr, v_num_elem, v_result): """Generate a CALL_MALLOC_GC(malloc_array_fn, ...) going either to the standard or the nonstandard version of the function.""" # if (arraydescr.basesize == self.gc_ll_descr.standard_array_basesize and arraydescr.lendescr.offset == self.gc_ll_descr.standard_array_length_ofs): # this is a standard-looking array, common case addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_array') args = [ ConstInt(addr), ConstInt(arraydescr.itemsize), ConstInt(arraydescr.tid), v_num_elem ] calldescr = self.gc_ll_descr.malloc_array_descr else: # rare case, so don't care too much about the number of arguments addr = self.gc_ll_descr.get_malloc_fn_addr( 'malloc_array_nonstandard') args = [ ConstInt(addr), ConstInt(arraydescr.basesize), ConstInt(arraydescr.itemsize), ConstInt(arraydescr.lendescr.offset), ConstInt(arraydescr.tid), v_num_elem ] calldescr = self.gc_ll_descr.malloc_array_nonstandard_descr self._gen_call_malloc_gc(args, v_result, calldescr) def gen_malloc_str(self, v_num_elem, v_result): """Generate a CALL_MALLOC_GC(malloc_str_fn, ...).""" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_str') self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result, self.gc_ll_descr.malloc_str_descr) def gen_malloc_unicode(self, v_num_elem, v_result): """Generate a CALL_MALLOC_GC(malloc_unicode_fn, ...).""" addr = self.gc_ll_descr.get_malloc_fn_addr('malloc_unicode') self._gen_call_malloc_gc([ConstInt(addr), v_num_elem], v_result, self.gc_ll_descr.malloc_unicode_descr) def gen_malloc_nursery_varsize(self, itemsize, v_length, v_result, arraydescr, kind=FLAG_ARRAY): """ itemsize is an int, v_length and v_result are boxes """ gc_descr = self.gc_ll_descr if (kind == FLAG_ARRAY and (arraydescr.basesize != gc_descr.standard_array_basesize or arraydescr.lendescr.offset != gc_descr.standard_array_length_ofs)): return False self.emitting_an_operation_that_can_collect() op = ResOperation( rop.CALL_MALLOC_NURSERY_VARSIZE, [ConstInt(kind), ConstInt(itemsize), v_length], v_result, descr=arraydescr) self.newops.append(op) # don't record v_result into self.write_barrier_applied: # it can be a large, young array with card marking, and then # the GC relies on the write barrier being called return True def gen_malloc_nursery_varsize_frame(self, sizebox, v_result): """ Generate CALL_MALLOC_NURSERY_VARSIZE_FRAME """ self.emitting_an_operation_that_can_collect() op = ResOperation(rop.CALL_MALLOC_NURSERY_VARSIZE_FRAME, [sizebox], v_result) self.newops.append(op) self.write_barrier_applied[v_result] = None def gen_malloc_nursery(self, size, v_result): """Try to generate or update a CALL_MALLOC_NURSERY. If that succeeds, return True; you still need to write the tid. If that fails, return False. """ size = self.round_up_for_allocation(size) if not self.gc_ll_descr.can_use_nursery_malloc(size): return False # op = None if self._op_malloc_nursery is not None: # already a MALLOC_NURSERY: increment its total size total_size = self._op_malloc_nursery.getarg(0).getint() total_size += size if self.gc_ll_descr.can_use_nursery_malloc(total_size): # if the total size is still reasonable, merge it self._op_malloc_nursery.setarg(0, ConstInt(total_size)) op = ResOperation(rop.INT_ADD, [ self._v_last_malloced_nursery, ConstInt(self._previous_size) ], v_result) if op is None: # if we failed to merge with a previous MALLOC_NURSERY, emit one self.emitting_an_operation_that_can_collect() op = ResOperation(rop.CALL_MALLOC_NURSERY, [ConstInt(size)], v_result) self._op_malloc_nursery = op # self.newops.append(op) self._previous_size = size self._v_last_malloced_nursery = v_result self.write_barrier_applied[v_result] = None return True def gen_initialize_tid(self, v_newgcobj, tid): if self.gc_ll_descr.fielddescr_tid is not None: # produce a SETFIELD to initialize the GC header op = ResOperation(rop.SETFIELD_GC, [v_newgcobj, ConstInt(tid)], None, descr=self.gc_ll_descr.fielddescr_tid) self.newops.append(op) def gen_initialize_len(self, v_newgcobj, v_length, arraylen_descr): # produce a SETFIELD to initialize the array length op = ResOperation(rop.SETFIELD_GC, [v_newgcobj, v_length], None, descr=arraylen_descr) self.newops.append(op) # ---------- def handle_write_barrier_setfield(self, op): val = op.getarg(0) if val not in self.write_barrier_applied: v = op.getarg(1) if (isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and rgc.needs_write_barrier(v.value))): self.gen_write_barrier(val) #op = op.copy_and_change(rop.SETFIELD_RAW) self.newops.append(op) def handle_write_barrier_setarrayitem(self, op): val = op.getarg(0) if val not in self.write_barrier_applied: v = op.getarg(2) if (isinstance(v, BoxPtr) or (isinstance(v, ConstPtr) and rgc.needs_write_barrier(v.value))): self.gen_write_barrier_array(val, op.getarg(1)) #op = op.copy_and_change(rop.SET{ARRAYITEM,INTERIORFIELD}_RAW) self.newops.append(op) handle_write_barrier_setinteriorfield = handle_write_barrier_setarrayitem def gen_write_barrier(self, v_base): write_barrier_descr = self.gc_ll_descr.write_barrier_descr args = [v_base] self.newops.append( ResOperation(rop.COND_CALL_GC_WB, args, None, descr=write_barrier_descr)) self.write_barrier_applied[v_base] = None def gen_write_barrier_array(self, v_base, v_index): write_barrier_descr = self.gc_ll_descr.write_barrier_descr if write_barrier_descr.has_write_barrier_from_array(self.cpu): # If we know statically the length of 'v', and it is not too # big, then produce a regular write_barrier. If it's unknown or # too big, produce instead a write_barrier_from_array. LARGE = 130 length = self.known_lengths.get(v_base, LARGE) if length >= LARGE: # unknown or too big: produce a write_barrier_from_array args = [v_base, v_index] self.newops.append( ResOperation(rop.COND_CALL_GC_WB_ARRAY, args, None, descr=write_barrier_descr)) # a WB_ARRAY is not enough to prevent any future write # barriers, so don't add to 'write_barrier_applied'! return # fall-back case: produce a write_barrier self.gen_write_barrier(v_base) def round_up_for_allocation(self, size): if not self.gc_ll_descr.round_up: return size if self.gc_ll_descr.translate_support_code: from rpython.rtyper.lltypesystem import llarena return llarena.round_up_for_allocation( size, self.gc_ll_descr.minimal_size_in_nursery) else: # non-translated: do it manually # assume that "self.gc_ll_descr.minimal_size_in_nursery" is 2 WORDs size = max(size, 2 * WORD) return (size + WORD - 1) & ~(WORD - 1) # round up