def produce_into(self, builder, r): v, A, v_index, descr = self.field_descr(builder, r) while True: if r.random() < 0.3: w = ConstInt(r.random_integer()) else: w = r.choice(builder.intvars) value = w.getint() if rffi.cast(lltype.Signed, rffi.cast(A.OF, value)) == value: break builder.do(self.opnum, [v, v_index, w], descr)
def gen_guard(self, builder, r): v = r.choice(builder.intvars) if r.random() > 0.8: other = r.choice(builder.intvars) else: if r.random() < 0.75: value = v.getint() elif r.random() < 0.5: value = v.getint() ^ 1 else: value = r.random_integer() other = ConstInt(value) op = ResOperation(self.opnum, [v, other]) return op, (v.getint() == other.getint())
def test_replace_box_with_const_in_array(self): h = HeapCache() box1 = RefFrontendOp(1) lengthbox2 = IntFrontendOp(2) lengthbox2.setint(10) h.arraylen_now_known(box1, lengthbox2) assert h.arraylen(box1) is lengthbox2 c10 = ConstInt(10) h.replace_box(lengthbox2, c10) assert c10.same_constant(h.arraylen(box1)) box2 = IntFrontendOp(2) box2.setint(12) h.setarrayitem(box1, index2, box2, descr1) assert h.getarrayitem(box1, index2, descr1) is box2 c12 = ConstInt(12) h.replace_box(box2, c12) assert c12.same_constant(h.getarrayitem(box1, index2, descr1))
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
from rpython.jit.metainterp.resoperation import rop from rpython.jit.metainterp.history import ConstInt, BoxInt, BasicFailDescr box1 = "box1" box2 = "box2" box3 = "box3" box4 = "box4" box5 = "box5" lengthbox1 = object() lengthbox2 = object() lengthbox3 = object() descr1 = object() descr2 = object() descr3 = object() index1 = ConstInt(0) index2 = ConstInt(1) class FakeEffectinfo(object): EF_ELIDABLE_CANNOT_RAISE = 0 #elidable function (and cannot raise) EF_LOOPINVARIANT = 1 #special: call it only once per loop EF_CANNOT_RAISE = 2 #a function which cannot raise EF_ELIDABLE_OR_MEMORYERROR = 3 EF_ELIDABLE_CAN_RAISE = 4 #elidable function (but can raise) EF_CAN_RAISE = 5 #normal function (can raise) EF_FORCES_VIRTUAL_OR_VIRTUALIZABLE = 6 #can raise and force virtualizables EF_RANDOM_EFFECTS = 7 #can do whatever OS_ARRAYCOPY = 0
def _append_debugging_code(self, operations, tp, number, token): counter = self._register_counter(tp, number, token) c_adr = ConstInt(rffi.cast(lltype.Signed, counter)) operations.append( ResOperation(rop.INCREMENT_DEBUG_COUNTER, [c_adr]))
def getstrlen(self, op, string_optimizer, mode): length = self.getstrlen1(mode) if length < 0: return None return ConstInt(length)
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 serialize_optimizer_knowledge(optimizer, numb_state, liveboxes, liveboxes_from_env, memo): from rpython.jit.metainterp.history import ConstInt available_boxes = {} for box in liveboxes: if box is not None and box in liveboxes_from_env: available_boxes[box] = None # class knowledge is stored as bits, true meaning the class is known, false # means unknown. on deserializing we look at the bits, and read the runtime # class for the known classes (which has to be the same in the bridge) and # mark that as known. this works for guard_class too: the class is only # known *after* the guard bitfield = 0 shifts = 0 for box in liveboxes: if box is None or box.type != "r": continue info = optimizer.getptrinfo(box) known_class = info is not None and info.get_known_class( optimizer.cpu) is not None bitfield <<= 1 bitfield |= known_class shifts += 1 if shifts == 6: numb_state.append_int(bitfield) bitfield = shifts = 0 if shifts: numb_state.append_int(bitfield << (6 - shifts)) # heap knowledge: we store triples of known heap fields in non-virtual # structs if optimizer.optheap: triples_struct, triples_array = optimizer.optheap.serialize_optheap( available_boxes) # can only encode descrs that have a known index into # metainterp_sd.all_descrs triples_struct = [ triple for triple in triples_struct if triple[1].descr_index != -1 ] numb_state.append_int(len(triples_struct)) for box1, descr, box2 in triples_struct: descr_index = descr.descr_index numb_state.append_short(tag_box(box1, liveboxes_from_env, memo)) numb_state.append_int(descr_index) numb_state.append_short(tag_box(box2, liveboxes_from_env, memo)) numb_state.append_int(len(triples_array)) for box1, index, descr, box2 in triples_array: descr_index = descr.descr_index numb_state.append_short(tag_box(box1, liveboxes_from_env, memo)) numb_state.append_int(index) numb_state.append_int(descr_index) numb_state.append_short(tag_box(box2, liveboxes_from_env, memo)) else: numb_state.append_int(0) numb_state.append_int(0) if optimizer.optrewrite: tuples_loopinvariant = optimizer.optrewrite.serialize_optrewrite( available_boxes) numb_state.append_int(len(tuples_loopinvariant)) for constarg0, box in tuples_loopinvariant: numb_state.append_short( tag_box(ConstInt(constarg0), liveboxes_from_env, memo)) numb_state.append_short(tag_box(box, liveboxes_from_env, memo)) else: numb_state.append_int(0)
def prepare_op_guard_no_exception(self, op): loc = self.make_sure_var_in_reg(ConstInt(self.cpu.pos_exception())) return [loc] + self._guard_impl(op)
def produce_into(self, builder, r): v, offset = self.field_descr(builder, r) builder.do(self.opnum, [v, ConstInt(offset)], None)
def test_virtual_adder_make_virtual(): b2s, b3s, b4s, b5s = [RefFrontendOp(0), IntFrontendOp(0), RefFrontendOp(0), RefFrontendOp(0)] c1s = ConstInt(111) storage = Storage() memo = ResumeDataLoopMemo(FakeMetaInterpStaticData()) modifier = ResumeDataVirtualAdder(FakeOptimizer(), storage, storage, None, memo) modifier.liveboxes_from_env = {} modifier.liveboxes = {} modifier.vfieldboxes = {} vdescr = LLtypeMixin.nodesize2 ca = ConstInt(ptr2int(LLtypeMixin.node_vtable2)) v4 = info.InstancePtrInfo(vdescr, ca, True) b4s.set_forwarded(v4) v4.setfield(LLtypeMixin.nextdescr, ca, b2s) v4.setfield(LLtypeMixin.valuedescr, ca, b3s) v4.setfield(LLtypeMixin.otherdescr, ca, b5s) ca = ConstInt(ptr2int(LLtypeMixin.node_vtable)) v2 = info.InstancePtrInfo(LLtypeMixin.nodesize, ca, True) v2.setfield(LLtypeMixin.nextdescr, b4s, ca) v2.setfield(LLtypeMixin.valuedescr, c1s, ca) b2s.set_forwarded(v2) modifier.register_virtual_fields(b2s, [c1s, None, None, None, b4s]) modifier.register_virtual_fields(b4s, [b3s, None, None, None, b2s, b5s]) liveboxes = [] modifier._number_virtuals(liveboxes, 0) storage.rd_consts = memo.consts[:] storage.rd_numb = Numbering([0]) # resume b3t, b5t = [IntFrontendOp(0), RefFrontendOp(0)] b5t.setref_base(demo55o) b3t.setint(33) newboxes = _resume_remap(liveboxes, [#b2s -- virtual b3s, #b4s -- virtual #b2s -- again, shared #b3s -- again, shared b5s], b3t, b5t) metainterp = MyMetaInterp() reader = ResumeDataFakeReader(storage, newboxes, metainterp) assert len(reader.virtuals_cache.virtuals_ptr_cache) == 2 b2t = reader.decode_ref(modifier._gettagged(b2s)) b4t = reader.decode_ref(modifier._gettagged(b4s)) trace = metainterp.trace b2new = (rop.NEW_WITH_VTABLE, [], b2t.getref_base(), LLtypeMixin.nodesize) b4new = (rop.NEW_WITH_VTABLE, [], b4t.getref_base(), LLtypeMixin.nodesize2) b2set = [(rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.nextdescr), (rop.SETFIELD_GC, [b2t, c1s], None, LLtypeMixin.valuedescr)] b4set = [(rop.SETFIELD_GC, [b4t, b2t], None, LLtypeMixin.nextdescr), (rop.SETFIELD_GC, [b4t, b3t], None, LLtypeMixin.valuedescr), (rop.SETFIELD_GC, [b4t, b5t], None, LLtypeMixin.otherdescr)] expected = [b2new, b4new] + b4set + b2set # check that we get the operations in 'expected', in a possibly different # order. assert len(trace) == len(expected) orig = trace[:] with CompareableConsts(): for x in trace: assert x in expected expected.remove(x) ptr = b2t.getref_base()._obj.container._as_ptr() assert lltype.typeOf(ptr) == lltype.Ptr(LLtypeMixin.NODE) assert ptr.value == 111 ptr2 = ptr.next ptr2 = lltype.cast_pointer(lltype.Ptr(LLtypeMixin.NODE2), ptr2) assert ptr2.other == demo55 assert ptr2.parent.value == 33 assert ptr2.parent.next == ptr
def test_ResumeDataLoopMemo_number(): b1, b2, b3, b4, b5 = [IntFrontendOp(0), IntFrontendOp(1), IntFrontendOp(2), RefFrontendOp(3), RefFrontendOp(4)] c1, c2, c3, c4 = [ConstInt(1), ConstInt(2), ConstInt(3), ConstInt(4)] env = [b1, c1, b2, b1, c2] metainterp_sd = FakeMetaInterpStaticData() t = Trace([b1, b2, b3, b4, b5], metainterp_sd) snap = t.create_snapshot(FakeJitCode("jitcode", 0), 0, Frame(env), False) env1 = [c3, b3, b1, c1] t.append(0) # descr index snap1 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame(env1), False, [], []) snap1.prev = snap env2 = [c3, b3, b1, c3] env3 = [c3, b3, b1, c3] env4 = [c3, b4, b1, c3] env5 = [b1, b4, b5] memo = ResumeDataLoopMemo(metainterp_sd) iter = t.get_iter() b1, b2, b3, b4, b5 = iter.inputargs numb_state = memo.number(0, iter) numb = numb_state.create_numbering() assert numb_state.num_virtuals == 0 assert numb_state.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b3: tag(2, TAGBOX)} base = [0, 0, tag(0, TAGBOX), tag(1, TAGINT), tag(1, TAGBOX), tag(0, TAGBOX), tag(2, TAGINT)] assert unpack_numbering(numb) == [17, 0, 0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), tag(1, TAGINT)] t.append(0) snap2 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame(env2), False, [], []) snap2.prev = snap numb_state2 = memo.number(1, iter) numb2 = numb_state2.create_numbering() assert numb_state2.num_virtuals == 0 assert numb_state2.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b3: tag(2, TAGBOX)} assert numb_state2.liveboxes is not numb_state.liveboxes assert unpack_numbering(numb2) == [17, 0, 0, 0] + base + [0, 2, tag(3, TAGINT), tag(2, TAGBOX), tag(0, TAGBOX), tag(3, TAGINT)] t.append(0) snap3 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame([]), False, [], env3) snap3.prev = snap class FakeVirtualInfo(info.AbstractVirtualPtrInfo): def __init__(self, virt): self._is_virtual = virt def is_virtual(self): return self._is_virtual # renamed b3.set_forwarded(c4) numb_state3 = memo.number(2, iter) numb3 = numb_state3.create_numbering() assert numb_state3.num_virtuals == 0 assert numb_state3.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX)} assert unpack_numbering(numb3) == ([17, 0, 0, 2, tag(3, TAGINT), tag(4, TAGINT), tag(0, TAGBOX), tag(3, TAGINT)] + base + [0, 2]) # virtual t.append(0) snap4 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 2, Frame([]), False, [], env4) snap4.prev = snap b4.set_forwarded(FakeVirtualInfo(True)) numb_state4 = memo.number(3, iter) numb4 = numb_state4.create_numbering() assert numb_state4.num_virtuals == 1 assert numb_state4.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b4: tag(0, TAGVIRTUAL)} assert unpack_numbering(numb4) == [17, 0, 0, 2, tag(3, TAGINT), tag(0, TAGVIRTUAL), tag(0, TAGBOX), tag(3, TAGINT)] + base + [0, 2] t.append(0) snap4 = t.create_snapshot(FakeJitCode("jitcode", 2), 1, Frame(env4), False) t.append(0) snap4.prev = snap snap5 = t.create_top_snapshot(FakeJitCode("jitcode", 0), 0, Frame([]), False, env5, []) snap5.prev = snap4 b4.set_forwarded(FakeVirtualInfo(True)) b5.set_forwarded(FakeVirtualInfo(True)) numb_state5 = memo.number(4, iter) numb5 = numb_state5.create_numbering() assert numb_state5.num_virtuals == 2 assert numb_state5.liveboxes == {b1: tag(0, TAGBOX), b2: tag(1, TAGBOX), b4: tag(0, TAGVIRTUAL), b5: tag(1, TAGVIRTUAL)} assert unpack_numbering(numb5) == [22, 0, 3, tag(0, TAGBOX), tag(0, TAGVIRTUAL), tag(1, TAGVIRTUAL), 0] + base + [ 2, 1, tag(3, TAGINT), tag(0, TAGVIRTUAL), tag(0, TAGBOX), tag(3, TAGINT) ] + [0, 0]
def test_virtual_adder_pending_fields_and_arrayitems(): class Storage(object): pass storage = Storage() modifier = ResumeDataVirtualAdder(None, storage, storage, None, None) modifier._add_pending_fields([]) assert not storage.rd_pendingfields # class FieldDescr(AbstractDescr): def is_array_of_primitives(self): return False field_a = FieldDescr() storage = Storage() modifier = ResumeDataVirtualAdder(None, storage, storage, None, None) a = IntFrontendOp(0) b = IntFrontendOp(0) modifier.liveboxes_from_env = {a: rffi.cast(rffi.SHORT, 1042), b: rffi.cast(rffi.SHORT, 1061)} modifier._add_pending_fields( [ResOperation(rop.SETFIELD_GC, [a, b], descr=field_a)]) pf = storage.rd_pendingfields assert len(pf) == 1 assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr) is field_a) assert rffi.cast(lltype.Signed, pf[0].num) == 1042 assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061 assert rffi.cast(lltype.Signed, pf[0].itemindex) == -1 # array_a = FieldDescr() storage = Storage() modifier = ResumeDataVirtualAdder(None, storage, storage, None, None) a42 = IntFrontendOp(0) a61 = IntFrontendOp(0) a62 = IntFrontendOp(0) a63 = IntFrontendOp(0) modifier.liveboxes_from_env = {a42: rffi.cast(rffi.SHORT, 1042), a61: rffi.cast(rffi.SHORT, 1061), a62: rffi.cast(rffi.SHORT, 1062), a63: rffi.cast(rffi.SHORT, 1063)} modifier._add_pending_fields([ ResOperation(rop.SETARRAYITEM_GC, [a42, ConstInt(0), a61], descr=array_a), ResOperation(rop.SETARRAYITEM_GC, [a42, ConstInt(2147483647), a62], descr=array_a)]) pf = storage.rd_pendingfields assert len(pf) == 2 assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[0].lldescr) is array_a) assert rffi.cast(lltype.Signed, pf[0].num) == 1042 assert rffi.cast(lltype.Signed, pf[0].fieldnum) == 1061 assert rffi.cast(lltype.Signed, pf[0].itemindex) == 0 assert (annlowlevel.cast_base_ptr_to_instance(FieldDescr, pf[1].lldescr) is array_a) assert rffi.cast(lltype.Signed, pf[1].num) == 1042 assert rffi.cast(lltype.Signed, pf[1].fieldnum) == 1062 assert rffi.cast(lltype.Signed, pf[1].itemindex) == 2147483647 # if sys.maxint >= 2147483648: with py.test.raises(TagOverflow): modifier._add_pending_fields( [ResOperation(rop.SETARRAYITEM_GC, [a42, ConstInt(2147483648), a63], descr=array_a)])
def test_loc_of_const(self): rm = RegisterManager({}) rm.next_instruction() assert isinstance(rm.loc(ConstInt(1)), ConstInt)
def get_funcbox(cls, cpu, func_ptr): return ConstInt(ptr2int(func_ptr))
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 oplist_no_descrs = 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 for op in oplist_no_descrs: if op.is_guard(): op.setdescr(None) 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 if pypy_hooks.are_hooks_enabled(): pypy_hooks.after_compile(di_loop) def interp_on_compile_bridge(): if pypy_hooks.are_hooks_enabled(): pypy_hooks.after_compile_bridge(di_bridge) def interp_on_optimize(): if pypy_hooks.are_hooks_enabled(): di_loop_optimize.oplist = cls.oplist pypy_hooks.before_compile(di_loop_optimize) def interp_on_abort(): if pypy_hooks.are_hooks_enabled(): pypy_hooks.on_abort(Counters.ABORT_TOO_LONG, pypyjitdriver, greenkey, 'blah', Logger(MockSD), cls.oplist_no_descrs) 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.orig_oplist_no_descrs = oplist_no_descrs 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_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
def test_stuff_followed_by_guard(self): boxes = [(InputArgInt(1), InputArgInt(0)), (InputArgInt(0), InputArgInt(1)), (InputArgInt(1), InputArgInt(1)), (InputArgInt(-1), InputArgInt(1)), (InputArgInt(1), InputArgInt(-1)), (ConstInt(1), InputArgInt(0)), (ConstInt(0), InputArgInt(1)), (ConstInt(1), InputArgInt(1)), (ConstInt(-1), InputArgInt(1)), (ConstInt(1), InputArgInt(-1)), (InputArgInt(1), ConstInt(0)), (InputArgInt(0), ConstInt(1)), (InputArgInt(1), ConstInt(1)), (InputArgInt(-1), ConstInt(1)), (InputArgInt(1), ConstInt(-1))] guards = [rop.GUARD_FALSE, rop.GUARD_TRUE] all = [ rop.INT_EQ, rop.INT_NE, rop.INT_LE, rop.INT_LT, rop.INT_GT, rop.INT_GE, rop.UINT_GT, rop.UINT_LT, rop.UINT_LE, rop.UINT_GE ] for a, b in boxes: for guard in guards: for op in all: i1 = ResOperation(rop.SAME_AS_I, [ConstInt(1)]) res = ResOperation(op, [a, b]) ops = [ i1, res, ResOperation(guard, [res], descr=BasicFailDescr()), ResOperation(rop.FINISH, [ConstInt(0)], descr=BasicFinalDescr()), ] ops[-2].setfailargs([i1]) inputargs = [i for i in (a, b) if not isinstance(i, Const)] looptoken = JitCellToken() self.cpu.compile_loop(inputargs, ops, looptoken) inputvalues = [box.getint() for box in inputargs] deadframe = self.cpu.execute_token(looptoken, *inputvalues) result = self.cpu.get_int_value(deadframe, 0) expected = execute(self.cpu, None, op, None, a, b) if guard == rop.GUARD_FALSE: assert result == expected else: assert result != expected
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 test_compile_bridge_check_profile_info(self): py.test.skip("does not work, reinvestigate") class FakeProfileAgent(object): def __init__(self): self.functions = [] def native_code_written(self, name, address, size): self.functions.append((name, address, size)) self.cpu.profile_agent = agent = FakeProfileAgent() i0 = InputArgInt() i1 = InputArgInt() i2 = InputArgInt() targettoken = TargetToken() faildescr1 = BasicFailDescr(1) faildescr2 = BasicFailDescr(2) looptoken = JitCellToken() looptoken.number = 17 class FakeString(object): def __init__(self, val): self.val = val def _get_str(self): return self.val operations = [ ResOperation(rop.LABEL, [i0], None, descr=targettoken), ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("hello"), 0, 0], None), ResOperation(rop.INT_ADD, [i0, ConstInt(1)], i1), ResOperation(rop.INT_LE, [i1, ConstInt(9)], i2), ResOperation(rop.GUARD_TRUE, [i2], None, descr=faildescr1), ResOperation(rop.JUMP, [i1], None, descr=targettoken), ] inputargs = [i0] operations[-2].setfailargs([i1]) self.cpu.compile_loop(inputargs, operations, looptoken) name, loopaddress, loopsize = agent.functions[0] assert name == "Loop # 17: hello (loop counter 0)" assert loopaddress <= looptoken._ll_loop_code assert loopsize >= 40 # randomish number i1b = InputArgInt() i3 = InputArgInt() bridge = [ ResOperation(rop.INT_LE, [i1b, ConstInt(19)], i3), ResOperation(rop.GUARD_TRUE, [i3], None, descr=faildescr2), ResOperation(rop.DEBUG_MERGE_POINT, [FakeString("bye"), 0, 0], None), ResOperation(rop.JUMP, [i1b], None, descr=targettoken), ] bridge[1].setfailargs([i1b]) self.cpu.compile_bridge(faildescr1, [i1b], bridge, looptoken) name, address, size = agent.functions[1] assert name == "Bridge # 0: bye (loop counter 1)" # Would be exactly ==, but there are some guard failure recovery # stubs in-between assert address >= loopaddress + loopsize assert size >= 10 # randomish number deadframe = self.cpu.execute_token(looptoken, 2) fail = self.cpu.get_latest_descr(deadframe) assert fail.identifier == 2 res = self.cpu.get_int_value(deadframe, 0) assert res == 20
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 test_calling_convention(self, monkeypatch): if WORD != 4: py.test.skip("32-bit only test") from rpython.jit.backend.x86.regloc import eax, edx from rpython.jit.backend.x86 import codebuf, callbuilder from rpython.jit.codewriter.effectinfo import EffectInfo from rpython.rlib.libffi import types, clibffi had_stdcall = hasattr(clibffi, 'FFI_STDCALL') if not had_stdcall: # not running on Windows, but we can still test monkeypatch.setattr(clibffi, 'FFI_STDCALL', 12345, raising=False) monkeypatch.setattr(callbuilder, 'stdcall_or_cdecl', True) else: assert callbuilder.stdcall_or_cdecl # for real_ffi, reported_ffi in [ (clibffi.FFI_DEFAULT_ABI, clibffi.FFI_DEFAULT_ABI), (clibffi.FFI_STDCALL, clibffi.FFI_DEFAULT_ABI), (clibffi.FFI_STDCALL, clibffi.FFI_STDCALL) ]: cpu = self.cpu mc = codebuf.MachineCodeBlockWrapper() mc.MOV_rs(eax.value, 4) # argument 1 mc.MOV_rs(edx.value, 40) # argument 10 mc.SUB_rr(eax.value, edx.value) # return arg1 - arg10 if real_ffi == clibffi.FFI_DEFAULT_ABI: mc.RET() else: mc.RET16_i(40) rawstart = mc.materialize(cpu, []) # calldescr = cpu._calldescr_dynamic_for_tests([types.slong] * 10, types.slong) calldescr.get_call_conv = lambda: reported_ffi # <==== hack # ^^^ we patch get_call_conv() so that the test also makes sense # on Linux, because clibffi.get_call_conv() would always # return FFI_DEFAULT_ABI on non-Windows platforms. funcbox = ConstInt(rawstart) i1 = InputArgInt() i2 = InputArgInt() c = ConstInt(-1) faildescr = BasicFailDescr(1) cz = ConstInt(0) # we must call it repeatedly: if the stack pointer gets increased # by 40 bytes by the STDCALL call, and if we don't expect it, # then we are going to get our stack emptied unexpectedly by # several repeated calls ops = [ ResOperation(rop.CALL_RELEASE_GIL_I, [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2], descr=calldescr), ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr), ResOperation(rop.CALL_RELEASE_GIL_I, [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2], descr=calldescr), ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr), ResOperation(rop.CALL_RELEASE_GIL_I, [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2], descr=calldescr), ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr), ResOperation(rop.CALL_RELEASE_GIL_I, [cz, funcbox, i1, c, c, c, c, c, c, c, c, i2], descr=calldescr), ResOperation(rop.GUARD_NOT_FORCED, [], descr=faildescr), ] i3 = ops[0] i4 = ops[2] i5 = ops[4] i6 = ops[6] ops += [ ResOperation(rop.GUARD_FALSE, [i3], descr=BasicFailDescr(0)), ResOperation(rop.FINISH, [], descr=BasicFinalDescr(1)) ] ops[-2].setfailargs([i3, i4, i5, i6]) ops[1].setfailargs([]) ops[3].setfailargs([]) ops[5].setfailargs([]) ops[7].setfailargs([]) looptoken = JitCellToken() self.cpu.compile_loop([i1, i2], ops, looptoken) deadframe = self.cpu.execute_token(looptoken, 123450, 123408) fail = self.cpu.get_latest_descr(deadframe) assert fail.identifier == 0 assert self.cpu.get_int_value(deadframe, 0) == 42 assert self.cpu.get_int_value(deadframe, 1) == 42 assert self.cpu.get_int_value(deadframe, 2) == 42 assert self.cpu.get_int_value(deadframe, 3) == 42
def test_get_deep_immutable_oplist(): a = ConstInt(1) b = ConstInt(2) ops = [rop.ResOperation(rop.rop.INT_ADD, [a, b])] newops = rop.get_deep_immutable_oplist(ops) py.test.raises(TypeError, "newops.append('foobar')") py.test.raises(TypeError, "newops[0] = 'foobar'") py.test.raises(AssertionError, "newops[0].setarg(0, 'd')") py.test.raises(AssertionError, "newops[0].setdescr('foobar')") VARI = rop.InputArgInt() VARF = rop.InputArgFloat() args = [ (rop.rop.INT_SIGNEXT, [VARI, ConstInt(2)], { 'from': INT_WORD, 'to': 2, 'cast_to': ('i', 2) }), (rop.rop.CAST_FLOAT_TO_INT, [VARF], { 'from': 8, 'to': 4 }), (rop.rop.CAST_SINGLEFLOAT_TO_FLOAT, [VARI], { 'from': 4, 'to': 8 }), (rop.rop.CAST_FLOAT_TO_SINGLEFLOAT, [VARF], { 'from': 8, 'to': 4
def test_bug_1(): 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() v20 = BoxInt() v21 = BoxInt() v22 = BoxInt() v23 = BoxInt() v24 = BoxInt() v25 = BoxInt() v26 = BoxInt() v27 = BoxInt() v28 = BoxInt() v29 = BoxInt() v30 = BoxInt() v31 = BoxInt() v32 = BoxInt() v33 = BoxInt() v34 = BoxInt() v35 = BoxInt() v36 = BoxInt() v37 = BoxInt() v38 = BoxInt() v39 = BoxInt() v40 = BoxInt() tmp41 = BoxInt() tmp42 = BoxInt() tmp43 = BoxInt() tmp44 = BoxInt() tmp45 = BoxInt() inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10] operations = [ ResOperation(rop.UINT_LT, [v6, ConstInt(0)], v11), ResOperation(rop.INT_AND, [v3, ConstInt(31)], tmp41), ResOperation(rop.INT_RSHIFT, [v3, tmp41], v12), ResOperation(rop.INT_NEG, [v2], v13), ResOperation(rop.INT_ADD, [v11, v7], v14), ResOperation(rop.INT_OR, [v3, v2], v15), ResOperation(rop.INT_OR, [v12, v12], v16), ResOperation(rop.INT_NE, [v2, v5], v17), ResOperation(rop.INT_AND, [v5, ConstInt(31)], tmp42), ResOperation(rop.UINT_RSHIFT, [v14, tmp42], v18), ResOperation(rop.INT_AND, [v14, ConstInt(31)], tmp43), ResOperation(rop.INT_LSHIFT, [ConstInt(7), tmp43], v19), ResOperation(rop.INT_NEG, [v19], v20), ResOperation(rop.INT_MOD, [v3, ConstInt(1)], v21), ResOperation(rop.UINT_GE, [v15, v1], v22), ResOperation(rop.INT_AND, [v16, ConstInt(31)], tmp44), ResOperation(rop.INT_LSHIFT, [v8, tmp44], v23), ResOperation(rop.INT_IS_TRUE, [v17], v24), ResOperation(rop.INT_AND, [v5, ConstInt(31)], tmp45), ResOperation(rop.INT_LSHIFT, [v14, tmp45], v25), ResOperation(rop.INT_LSHIFT, [v5, ConstInt(17)], v26), ResOperation(rop.INT_EQ, [v9, v15], v27), ResOperation(rop.INT_GE, [ConstInt(0), v6], v28), ResOperation(rop.INT_NEG, [v15], v29), ResOperation(rop.INT_NEG, [v22], v30), ResOperation(rop.INT_ADD, [v7, v16], v31), ResOperation(rop.UINT_LT, [v19, v19], v32), ResOperation(rop.INT_ADD, [v2, ConstInt(1)], v33), ResOperation(rop.INT_NEG, [v5], v34), ResOperation(rop.INT_ADD, [v17, v24], v35), ResOperation(rop.UINT_LT, [ConstInt(2), v16], v36), ResOperation(rop.INT_NEG, [v9], v37), ResOperation(rop.INT_GT, [v4, v11], v38), ResOperation(rop.INT_LT, [v27, v22], v39), ResOperation(rop.INT_NEG, [v27], v40), ResOperation(rop.GUARD_FALSE, [v1], None, descr=BasicFailDescr()), ] operations[-1].setfailargs([ v40, v10, v36, v26, v13, v30, v21, v33, v18, v25, v31, v32, v28, v29, v35, v38, v20, v39, v34, v23, v37 ]) cpu = CPU(None, None) cpu.setup_once() looptoken = JitCellToken() cpu.compile_loop(inputargs, operations, looptoken) args = [17, -20, -6, 6, 1, 13, 13, 9, 49, 8] deadframe = cpu.execute_token(looptoken, *args) assert cpu.get_int_value(deadframe, 0) == 0 assert cpu.get_int_value(deadframe, 1) == 8 assert cpu.get_int_value(deadframe, 2) == 1 assert cpu.get_int_value(deadframe, 3) == 131072 assert cpu.get_int_value(deadframe, 4) == 20 assert cpu.get_int_value(deadframe, 5) == -1 assert cpu.get_int_value(deadframe, 6) == 0 assert cpu.get_int_value(deadframe, 7) == -19 assert cpu.get_int_value(deadframe, 8) == 6 assert cpu.get_int_value(deadframe, 9) == 26 assert cpu.get_int_value(deadframe, 10) == 12 assert cpu.get_int_value(deadframe, 11) == 0 assert cpu.get_int_value(deadframe, 12) == 0 assert cpu.get_int_value(deadframe, 13) == 2 assert cpu.get_int_value(deadframe, 14) == 2 assert cpu.get_int_value(deadframe, 15) == 1 assert cpu.get_int_value(deadframe, 16) == -57344 assert cpu.get_int_value(deadframe, 17) == 1 assert cpu.get_int_value(deadframe, 18) == -1 if WORD == 4: assert cpu.get_int_value(deadframe, 19) == -2147483648 elif WORD == 8: assert cpu.get_int_value(deadframe, 19) == 19327352832 assert cpu.get_int_value(deadframe, 20) == -49
def prepare_op_guard_no_exception(self, op, fcond): loc = self.make_sure_var_in_reg(ConstInt(self.cpu.pos_exception())) arglocs = self._prepare_guard(op, [loc]) return arglocs
def test_bug_0(): 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() v20 = BoxInt() v21 = BoxInt() v22 = BoxInt() v23 = BoxInt() v24 = BoxInt() v25 = BoxInt() v26 = BoxInt() v27 = BoxInt() v28 = BoxInt() v29 = BoxInt() v30 = BoxInt() v31 = BoxInt() v32 = BoxInt() v33 = BoxInt() v34 = BoxInt() v35 = BoxInt() v36 = BoxInt() v37 = BoxInt() v38 = BoxInt() v39 = BoxInt() v40 = BoxInt() tmp41 = BoxInt() tmp42 = BoxInt() tmp43 = BoxInt() tmp44 = BoxInt() tmp45 = BoxInt() tmp46 = BoxInt() inputargs = [v1, v2, v3, v4, v5, v6, v7, v8, v9, v10] operations = [ ResOperation(rop.UINT_GT, [v3, ConstInt(-48)], v11), ResOperation(rop.INT_XOR, [v8, v1], v12), ResOperation(rop.INT_GT, [v6, ConstInt(-9)], v13), ResOperation(rop.INT_LE, [v13, v2], v14), ResOperation(rop.INT_LE, [v11, v5], v15), ResOperation(rop.UINT_GE, [v13, v13], v16), ResOperation(rop.INT_OR, [v9, ConstInt(-23)], v17), ResOperation(rop.INT_LT, [v10, v13], v18), ResOperation(rop.INT_OR, [v15, v5], v19), ResOperation(rop.INT_XOR, [v17, ConstInt(54)], v20), ResOperation(rop.INT_MUL, [v8, v10], v21), ResOperation(rop.INT_OR, [v3, v9], v22), ResOperation(rop.INT_AND, [v11, ConstInt(-4)], tmp41), ResOperation(rop.INT_OR, [tmp41, ConstInt(1)], tmp42), ResOperation(rop.INT_MOD, [v12, tmp42], v23), ResOperation(rop.INT_IS_TRUE, [v6], v24), ResOperation(rop.UINT_RSHIFT, [v15, ConstInt(6)], v25), ResOperation(rop.INT_OR, [ConstInt(-4), v25], v26), ResOperation(rop.INT_INVERT, [v8], v27), ResOperation(rop.INT_SUB, [ConstInt(-113), v11], v28), ResOperation(rop.INT_NEG, [v7], v29), ResOperation(rop.INT_NEG, [v24], v30), ResOperation(rop.INT_FLOORDIV, [v3, ConstInt(53)], v31), ResOperation(rop.INT_MUL, [v28, v27], v32), ResOperation(rop.INT_AND, [v18, ConstInt(-4)], tmp43), ResOperation(rop.INT_OR, [tmp43, ConstInt(1)], tmp44), ResOperation(rop.INT_MOD, [v26, tmp44], v33), ResOperation(rop.INT_OR, [v27, v19], v34), ResOperation(rop.UINT_LT, [v13, ConstInt(1)], v35), ResOperation(rop.INT_AND, [v21, ConstInt(31)], tmp45), ResOperation(rop.INT_RSHIFT, [v21, tmp45], v36), ResOperation(rop.INT_AND, [v20, ConstInt(31)], tmp46), ResOperation(rop.UINT_RSHIFT, [v4, tmp46], v37), ResOperation(rop.UINT_GT, [v33, ConstInt(-11)], v38), ResOperation(rop.INT_NEG, [v7], v39), ResOperation(rop.INT_GT, [v24, v32], v40), ResOperation(rop.GUARD_FALSE, [v1], None, descr=BasicFailDescr()), ] operations[-1].setfailargs( [v40, v36, v37, v31, v16, v34, v35, v23, v22, v29, v14, v39, v30, v38]) cpu = CPU(None, None) cpu.setup_once() looptoken = JitCellToken() cpu.compile_loop(inputargs, operations, looptoken) args = [-13, 10, 10, 8, -8, -16, -18, 46, -12, 26] deadframe = cpu.execute_token(looptoken, *args) assert cpu.get_int_value(deadframe, 0) == 0 assert cpu.get_int_value(deadframe, 1) == 0 assert cpu.get_int_value(deadframe, 2) == 0 assert cpu.get_int_value(deadframe, 3) == 0 assert cpu.get_int_value(deadframe, 4) == 1 assert cpu.get_int_value(deadframe, 5) == -7 assert cpu.get_int_value(deadframe, 6) == 1 assert cpu.get_int_value(deadframe, 7) == 0 assert cpu.get_int_value(deadframe, 8) == -2 assert cpu.get_int_value(deadframe, 9) == 18 assert cpu.get_int_value(deadframe, 10) == 1 assert cpu.get_int_value(deadframe, 11) == 18 assert cpu.get_int_value(deadframe, 12) == -1 assert cpu.get_int_value(deadframe, 13) == 0
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 getstrlen(self, op, string_optimizer, mode): assert op is not None if self.lgtop is None: self.lgtop = ConstInt(len(self._chars)) return self.lgtop
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 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 optimize_NEW_WITH_VTABLE(self, op): known_class = ConstInt(op.getdescr().get_vtable()) self.make_virtual(known_class, op, op.getdescr())
def cls_of_box(self, box): obj = lltype.cast_opaque_ptr(OBJECTPTR, box.getref_base()) return ConstInt(ptr2int(obj.typeptr))