def produce_potential_short_preamble_ops(self, optimizer, shortboxes, descr): if self._lazy_setfield is not None: return for structvalue in self._cached_fields_getfield_op.keys(): op = self._cached_fields_getfield_op[structvalue] if not op: continue if optimizer.getvalue(op.getarg(0)) in optimizer.opaque_pointers: continue if structvalue in self._cached_fields: if op.getopnum() == rop.SETFIELD_GC: result = op.getarg(1) if isinstance(result, Const): newresult = result.clonebox() optimizer.make_constant(newresult, result) result = newresult getop = ResOperation(rop.GETFIELD_GC, [op.getarg(0)], result, op.getdescr()) shortboxes.add_potential(getop, synthetic=True) if op.getopnum() == rop.SETARRAYITEM_GC: result = op.getarg(2) if isinstance(result, Const): newresult = result.clonebox() optimizer.make_constant(newresult, result) result = newresult getop = ResOperation( rop.GETARRAYITEM_GC, [op.getarg(0), op.getarg(1)], result, op.getdescr()) shortboxes.add_potential(getop, synthetic=True) elif op.result is not None: shortboxes.add_potential(op)
def copy_str_content(string_optimizer, srcbox, targetbox, srcoffsetbox, offsetbox, lengthbox, mode, need_next_offset=True): if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const): M = 5 else: M = 2 if isinstance(lengthbox, ConstInt) and lengthbox.value <= M: # up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM # instead of just a COPYSTRCONTENT. for i in range(lengthbox.value): charbox = _strgetitem(string_optimizer, srcbox, srcoffsetbox, mode) srcoffsetbox = _int_add(string_optimizer, srcoffsetbox, CONST_1) assert isinstance(targetbox, BoxPtr) # ConstPtr never makes sense string_optimizer.emit_operation( ResOperation(mode.STRSETITEM, [targetbox, offsetbox, charbox], None)) offsetbox = _int_add(string_optimizer, offsetbox, CONST_1) else: if need_next_offset: nextoffsetbox = _int_add(string_optimizer, offsetbox, lengthbox) else: nextoffsetbox = None assert isinstance(targetbox, BoxPtr) # ConstPtr never makes sense op = ResOperation( mode.COPYSTRCONTENT, [srcbox, targetbox, srcoffsetbox, offsetbox, lengthbox], None) string_optimizer.emit_operation(op) offsetbox = nextoffsetbox return offsetbox
def has_pure_result(self, opnum, args, descr): op = ResOperation(opnum, args, None, descr) key = self.optimizer.make_args_key(op) op = self.optimizer.pure_operations.get(key, None) if op is None: return False return op.getdescr() is descr
def gen_malloc_nursery(self, size, v_result): """Try to generate or update a CALL_MALLOC_NURSERY. If that fails, generate a plain CALL_MALLOC_GC instead. """ 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.recent_mallocs[v_result] = None return True
def patch_new_loop_to_load_virtualizable_fields(loop, jitdriver_sd): vinfo = jitdriver_sd.virtualizable_info extra_ops = [] inputargs = loop.inputargs vable_box = inputargs[jitdriver_sd.index_of_virtualizable] i = jitdriver_sd.num_red_args loop.inputargs = inputargs[:i] for descr in vinfo.static_field_descrs: assert i < len(inputargs) box = inputargs[i] extra_ops.append(ResOperation(rop.GETFIELD_GC, [vable_box], box, descr)) i += 1 arrayindex = 0 for descr in vinfo.array_field_descrs: vable = vable_box.getref_base() arraylen = vinfo.get_array_length(vable, arrayindex) arraybox = BoxPtr() extra_ops.append( ResOperation(rop.GETFIELD_GC, [vable_box], arraybox, descr)) arraydescr = vinfo.array_descrs[arrayindex] assert i + arraylen <= len(inputargs) for index in range(arraylen): box = inputargs[i] extra_ops.append( ResOperation(rop.GETARRAYITEM_GC, [arraybox, ConstInt(index)], box, descr=arraydescr)) i += 1 arrayindex += 1 assert i == len(inputargs) loop.operations = extra_ops + loop.operations
def find_rewritable_bool(self, op, args): try: oldopnum = opboolinvers[op.getopnum()] except KeyError: pass else: targs = self.optimizer.make_args_key( ResOperation(oldopnum, [args[0], args[1]], None)) if self.try_boolinvers(op, targs): return True try: oldopnum = opboolreflex[ op.getopnum()] # FIXME: add INT_ADD, INT_MUL except KeyError: pass else: targs = self.optimizer.make_args_key( ResOperation(oldopnum, [args[1], args[0]], None)) oldop = self.get_pure_result(targs) if oldop is not None and oldop.getdescr() is op.getdescr(): self.make_equal_to(op.result, self.getvalue(oldop.result)) return True try: oldopnum = opboolinvers[opboolreflex[op.getopnum()]] except KeyError: pass else: targs = self.optimizer.make_args_key( ResOperation(oldopnum, [args[1], args[0]], None)) if self.try_boolinvers(op, targs): return True return False
def get_token_for_call(self, cpu): if self.loop_token is not None: return self.loop_token args = [BoxInt()] + self.instantiate_arg_classes() if self.get_result_size(cpu.translate_support_code) == 0: result = None result_list = [] else: if self.returns_a_pointer(): result = BoxPtr() elif self.returns_a_float(): result = BoxFloat() else: result = BoxInt() result_list = [result] operations = [ ResOperation(rop.CALL, args[:], result, self), ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=BasicFailDescr()), ResOperation(rop.FINISH, result_list, None, descr=BasicFailDescr()) ] operations[1].fail_args = [] loop_token = LoopToken() # note: the 'args' that we pass below is not the same object as the # 'args[:]' that was passed above to ResOperation, because we want # the argument to ResOperation to be non-resizable, but the argument # to compile_loop to be resizable. cpu.compile_loop(args, operations, loop_token) self.loop_token = loop_token return loop_token
def store_final_boxes_in_guard(self, op): descr = op.getdescr() assert isinstance(descr, compile.ResumeGuardDescr) modifier = resume.ResumeDataVirtualAdder(descr, self.resumedata_memo) newboxes = modifier.finish(self.values, self.pendingfields) if len(newboxes) > self.metainterp_sd.options.failargs_limit: # XXX be careful here compile.giveup() descr.store_final_boxes(op, newboxes) # if op.getopnum() == rop.GUARD_VALUE: if self.getvalue(op.getarg(0)) in self.bool_boxes: # Hack: turn guard_value(bool) into guard_true/guard_false. # This is done after the operation is emitted to let # store_final_boxes_in_guard set the guard_opnum field of the # descr to the original rop.GUARD_VALUE. constvalue = op.getarg(1).getint() if constvalue == 0: opnum = rop.GUARD_FALSE elif constvalue == 1: opnum = rop.GUARD_TRUE else: raise AssertionError("uh?") newop = ResOperation(opnum, [op.getarg(0)], op.result, descr) newop.setfailargs(op.getfailargs()) return newop else: # a real GUARD_VALUE. Make it use one counter per value. descr.make_a_counter_per_value(op) return op
def has_pure_result(self, opnum, args, descr): op = ResOperation(opnum, args, None, descr) key = self.optimizer.make_args_key(op) op = self.pure_operations.get(key, None) if op is None: return False return op.getdescr() is descr
def store_final_boxes_in_guard(self, op): descr = op.getdescr() assert isinstance(descr, compile.ResumeGuardDescr) modifier = resume.ResumeDataVirtualAdder(descr, self.resumedata_memo) try: newboxes = modifier.finish(self, self.pendingfields) if len(newboxes) > self.metainterp_sd.options.failargs_limit: raise resume.TagOverflow except resume.TagOverflow: raise compile.giveup() descr.store_final_boxes(op, newboxes) # if op.getopnum() == rop.GUARD_VALUE: if self.getvalue(op.getarg(0)) in self.bool_boxes: # Hack: turn guard_value(bool) into guard_true/guard_false. # This is done after the operation is emitted to let # store_final_boxes_in_guard set the guard_opnum field of the # descr to the original rop.GUARD_VALUE. constvalue = op.getarg(1).getint() if constvalue == 0: opnum = rop.GUARD_FALSE elif constvalue == 1: opnum = rop.GUARD_TRUE else: raise AssertionError("uh?") newop = ResOperation(opnum, [op.getarg(0)], op.result, descr) newop.setfailargs(op.getfailargs()) return newop else: # a real GUARD_VALUE. Make it use one counter per value. descr.make_a_counter_per_value(op) return op
def optimize_VIRTUAL_REF_FINISH(self, op): # This operation is used in two cases. In normal cases, it # is the end of the frame, and op.getarg(1) is NULL. In this # case we just clear the vref.virtual_token, because it contains # a stack frame address and we are about to leave the frame. # In that case vref.forced should still be NULL, and remains # NULL; and accessing the frame through the vref later is # *forbidden* and will raise InvalidVirtualRef. # # In the other (uncommon) case, the operation is produced # earlier, because the vref was forced during tracing already. # In this case, op.getarg(1) is the virtual to force, and we # have to store it in vref.forced. # vrefinfo = self.optimizer.metainterp_sd.virtualref_info seo = self.optimizer.send_extra_operation # - set 'forced' to point to the real object objbox = op.getarg(1) if not self.optimizer.cpu.ts.CONST_NULL.same_constant(objbox): seo(ResOperation(rop.SETFIELD_GC, op.getarglist(), None, descr = vrefinfo.descr_forced)) # - set 'virtual_token' to TOKEN_NONE args = [op.getarg(0), ConstInt(vrefinfo.TOKEN_NONE)] seo(ResOperation(rop.SETFIELD_GC, args, None, descr = vrefinfo.descr_virtual_token))
def get_token_for_call(self, cpu): if self.loop_token is not None: return self.loop_token args = [BoxInt()] + self.instantiate_arg_classes() if self.get_result_size(cpu.translate_support_code) == 0: result = None result_list = [] else: if self.returns_a_pointer(): result = BoxPtr() elif self.returns_a_float(): result = BoxFloat() else: result = BoxInt() result_list = [result] operations = [ ResOperation(rop.CALL, args, result, self), ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=BasicFailDescr()), ResOperation(rop.FINISH, result_list, None, descr=BasicFailDescr())] operations[1].fail_args = [] loop_token = LoopToken() cpu.compile_loop(args, operations, loop_token) self.loop_token = loop_token return loop_token
def export_state(self, targetop): original_jump_args = targetop.getarglist() jump_args = [ self.getvalue(a).get_key_box() for a in original_jump_args ] assert self.optimizer.loop.resume_at_jump_descr resume_at_jump_descr = self.optimizer.loop.resume_at_jump_descr.clone_if_mutable( ) assert isinstance(resume_at_jump_descr, ResumeGuardDescr) resume_at_jump_descr.rd_snapshot = self.fix_snapshot( jump_args, resume_at_jump_descr.rd_snapshot) modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(jump_args) values = [self.getvalue(arg) for arg in jump_args] inputargs = virtual_state.make_inputargs(values, self.optimizer) short_inputargs = virtual_state.make_inputargs(values, self.optimizer, keyboxes=True) if self.boxes_created_this_iteration is not None: for box in self.inputargs: self.boxes_created_this_iteration[box] = True short_boxes = ShortBoxes(self.optimizer, inputargs, self.boxes_created_this_iteration) self.optimizer.clear_newoperations() for i in range(len(original_jump_args)): if values[i].is_virtual(): values[i].force_box(self.optimizer) if original_jump_args[i] is not jump_args[i]: op = ResOperation(rop.SAME_AS, [jump_args[i]], original_jump_args[i]) self.optimizer.emit_operation(op) inputarg_setup_ops = self.optimizer.get_newoperations() target_token = targetop.getdescr() assert isinstance(target_token, TargetToken) targetop.initarglist(inputargs) target_token.virtual_state = virtual_state target_token.short_preamble = [ ResOperation(rop.LABEL, short_inputargs, None) ] target_token.resume_at_jump_descr = resume_at_jump_descr exported_values = {} for box in inputargs: exported_values[box] = self.optimizer.getvalue(box) for op in short_boxes.operations(): if op and op.result: box = op.result exported_values[box] = self.optimizer.getvalue(box) target_token.exported_state = ExportedState(short_boxes, inputarg_setup_ops, exported_values)
def rewrite_assembler(self, cpu, operations): # Perform two kinds of rewrites in parallel: # # - Add COND_CALLs to the write barrier before SETFIELD_GC and # SETARRAYITEM_GC operations. # # - Remove all uses of ConstPtrs away from the assembler. # Idea: when running on a moving GC, we can't (easily) encode # the ConstPtrs in the assembler, because they can move at any # point in time. Instead, we store them in 'gcrefs.list', a GC # but nonmovable list; and here, we modify 'operations' to # replace direct usage of ConstPtr with a BoxPtr loaded by a # GETFIELD_RAW from the array 'gcrefs.list'. # newops = [] for op in operations: if op.opnum == rop.DEBUG_MERGE_POINT: continue # ---------- replace ConstPtrs with GETFIELD_RAW ---------- # xxx some performance issue here for i in range(len(op.args)): v = op.args[i] if isinstance(v, ConstPtr) and bool(v.value): addr = self.gcrefs.get_address_of_gcref(v.value) # ^^^even for non-movable objects, to record their presence if rgc.can_move(v.value): box = BoxPtr(v.value) addr = cpu.cast_adr_to_int(addr) newops.append( ResOperation(rop.GETFIELD_RAW, [ConstInt(addr)], box, self.single_gcref_descr)) op.args[i] = box # ---------- write barrier for SETFIELD_GC ---------- if op.opnum == rop.SETFIELD_GC: v = op.args[1] if isinstance( v, BoxPtr) or (isinstance(v, ConstPtr) and bool(v.value)): # store a non-NULL self._gen_write_barrier(newops, op.args[0], v) op = ResOperation(rop.SETFIELD_RAW, op.args, None, descr=op.descr) # ---------- write barrier for SETARRAYITEM_GC ---------- if op.opnum == rop.SETARRAYITEM_GC: v = op.args[2] if isinstance( v, BoxPtr) or (isinstance(v, ConstPtr) and bool(v.value)): # store a non-NULL self._gen_write_barrier(newops, op.args[0], v) op = ResOperation(rop.SETARRAYITEM_RAW, op.args, None, descr=op.descr) # ---------- newops.append(op) del operations[:] operations.extend(newops)
def convert_old_style_to_targets(loop, jump): newloop = TreeLoop(loop.name) newloop.inputargs = loop.inputargs newloop.operations = [ResOperation(rop.LABEL, loop.inputargs, None, descr=FakeDescr())] + \ loop.operations if not jump: assert newloop.operations[-1].getopnum() == rop.JUMP newloop.operations[-1] = ResOperation(rop.LABEL, newloop.operations[-1].getarglist(), None, descr=FakeDescr()) return newloop
def exc_handling(guard_op): # operations need to start with correct GUARD_EXCEPTION if guard_op._exc_box is None: op = ResOperation(rop.GUARD_NO_EXCEPTION, [], None) else: op = ResOperation(rop.GUARD_EXCEPTION, [guard_op._exc_box], BoxPtr()) op.descr = BasicFailDescr() op.fail_args = [] return op
def optimize_NEW_ARRAY(self, op): sizebox = self.get_constant_box(op.getarg(0)) if sizebox is not None: # if the original 'op' did not have a ConstInt as argument, # build a new one with the ConstInt argument if not isinstance(op.getarg(0), ConstInt): op = ResOperation(rop.NEW_ARRAY, [sizebox], op.result, descr=op.getdescr()) self.make_varray(op.getdescr(), sizebox.getint(), op.result, op) else: self.getvalue(op.result).ensure_nonnull() self.emit_operation(op)
def parse_result_op(self, line): res, op = line.split("=", 1) res = res.strip() op = op.strip() opnum, args, descr, fail_args = self.parse_op(op) if res in self.vars: raise ParseError("Double assign to var %s in line: %s" % (res, line)) rvar = self.box_for_var(res) self.vars[res] = rvar res = ResOperation(opnum, args, rvar, descr) res.fail_args = fail_args return res
def compile_tmp_callback(cpu, jitdriver_sd, greenboxes, redargtypes, memory_manager=None): """Make a LoopToken that corresponds to assembler code that just calls back the interpreter. Used temporarily: a fully compiled version of the code may end up replacing it. """ jitcell_token = make_jitcell_token(jitdriver_sd) nb_red_args = jitdriver_sd.num_red_args assert len(redargtypes) == nb_red_args inputargs = [] for kind in redargtypes: if kind == history.INT: box = BoxInt() elif kind == history.REF: box = BoxPtr() elif kind == history.FLOAT: box = BoxFloat() else: raise AssertionError inputargs.append(box) k = jitdriver_sd.portal_runner_adr funcbox = history.ConstInt(heaptracker.adr2int(k)) callargs = [funcbox] + greenboxes + inputargs # result_type = jitdriver_sd.result_type if result_type == history.INT: result = BoxInt() elif result_type == history.REF: result = BoxPtr() elif result_type == history.FLOAT: result = BoxFloat() elif result_type == history.VOID: result = None else: assert 0, "bad result_type" if result is not None: finishargs = [result] else: finishargs = [] # jd = jitdriver_sd faildescr = PropagateExceptionDescr() operations = [ ResOperation(rop.CALL, callargs, result, descr=jd.portal_calldescr), ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=faildescr), ResOperation(rop.FINISH, finishargs, None, descr=jd.portal_finishtoken) ] operations[1].setfailargs([]) operations = get_deep_immutable_oplist(operations) cpu.compile_loop(inputargs, operations, jitcell_token, log=False) if memory_manager is not None: # for tests memory_manager.keep_loop_alive(jitcell_token) return jitcell_token
def _optimize_NEWSTR(self, op, mode): length_box = self.get_constant_box(op.getarg(0)) if length_box: # if the original 'op' did not have a ConstInt as argument, # build a new one with the ConstInt argument if not isinstance(op.getarg(0), ConstInt): op = ResOperation(mode.NEWSTR, [length_box], op.result) vvalue = self.make_vstring_plain(op.result, op, mode) vvalue.setup(length_box.getint()) else: self.getvalue(op.result).ensure_nonnull() self.emit_operation(op) self.pure(mode.STRLEN, [op.result], op.getarg(0))
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 produce_into(self, builder, r): fail_subset = builder.subset_of_intvars(r) subset, f, exc = self.raising_func_code(builder, r) TP = lltype.FuncType([lltype.Signed] * len(subset), lltype.Void) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT) self.put(builder, args, descr) exc_box = ConstAddr(llmemory.cast_ptr_to_adr(exc), builder.cpu) op = ResOperation(rop.GUARD_EXCEPTION, [exc_box], BoxPtr(), descr=BasicFailDescr()) op.setfailargs(fail_subset) builder.loop.operations.append(op)
def make_guards(self, box, guards): if self.has_lower and self.lower > MININT: bound = self.lower res = BoxInt() op = ResOperation(rop.INT_GE, [box, ConstInt(bound)], res) guards.append(op) op = ResOperation(rop.GUARD_TRUE, [res], None) guards.append(op) if self.has_upper and self.upper < MAXINT: bound = self.upper res = BoxInt() op = ResOperation(rop.INT_LE, [box, ConstInt(bound)], res) guards.append(op) op = ResOperation(rop.GUARD_TRUE, [res], None) guards.append(op)
def produce_into(self, builder, r): fail_subset = builder.subset_of_intvars(r) subset, f, exc = self.raising_func_code(builder, r) TP = lltype.FuncType([lltype.Signed] * len(subset), lltype.Void) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = self.getcalldescr(builder, TP) self.put(builder, args, descr) exc_box = ConstAddr(llmemory.cast_ptr_to_adr(exc), builder.cpu) op = ResOperation(rop.GUARD_EXCEPTION, [exc_box], BoxPtr(), descr=BasicFailDescr()) op.setfailargs(fail_subset) builder.loop.operations.append(op)
def produce_into(self, builder, r): subset, f, exc = self.raising_func_code(builder, r) TP = lltype.FuncType([lltype.Signed] * len(subset), lltype.Void) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = self.getcalldescr(builder, TP) self.put(builder, args, descr) op = ResOperation(rop.GUARD_NO_EXCEPTION, [], BoxPtr(), descr=BasicFailDescr()) op._exc_box = ConstAddr(llmemory.cast_ptr_to_adr(exc), builder.cpu) op.setfailargs(builder.subset_of_intvars(r)) builder.should_fail_by = op builder.guard_op = op builder.loop.operations.append(op)
def generate_modified_call(self, oopspecindex, args, result, mode): oopspecindex += mode.OS_offset cic = self.optimizer.metainterp_sd.callinfocollection calldescr, func = cic.callinfo_for_oopspec(oopspecindex) op = ResOperation(rop.CALL, [ConstInt(func)] + args, result, descr=calldescr) self.emit_operation(op)
def create_op(self, opnum, args, result, descr): if opnum == ESCAPE_OP.OPNUM: return ESCAPE_OP(opnum, args, result, descr) if opnum == FORCE_SPILL.OPNUM: return FORCE_SPILL(opnum, args, result, descr) else: return ResOperation(opnum, args, result, descr)
def _optimize_CALL_ARRAYCOPY(self, op): source_value = self.getvalue(op.getarg(1)) dest_value = self.getvalue(op.getarg(2)) source_start_box = self.get_constant_box(op.getarg(3)) dest_start_box = self.get_constant_box(op.getarg(4)) length = self.get_constant_box(op.getarg(5)) if (source_value.is_virtual() and source_start_box and dest_start_box and length and (dest_value.is_virtual() or length.getint() <= 8)): from pypy.jit.metainterp.optimizeopt.virtualize import VArrayValue assert isinstance(source_value, VArrayValue) source_start = source_start_box.getint() dest_start = dest_start_box.getint() for index in range(length.getint()): val = source_value.getitem(index + source_start) if dest_value.is_virtual(): dest_value.setitem(index + dest_start, val) else: newop = ResOperation(rop.SETARRAYITEM_GC, [ op.getarg(2), ConstInt(index + dest_start), val.get_key_box() ], None, descr=source_value.arraydescr) self.emit_operation(newop) return True if length and length.getint() == 0: return True # 0-length arraycopy return False
def _gen_write_barrier(self, cpu, newops, v_base, v_value): v_tid = BoxInt() newops.append( ResOperation(rop.GETFIELD_RAW, [v_base], v_tid, descr=self.fielddescr_tid)) llop1 = self.llop1 funcptr = llop1.get_write_barrier_failing_case(self.WB_FUNCPTR) funcaddr = llmemory.cast_ptr_to_adr(funcptr) c_func = ConstInt(cpu.cast_adr_to_int(funcaddr)) args = [v_tid, self.c_jit_wb_if_flag, c_func, v_base, v_value] newops.append( ResOperation(rop.COND_CALL_GC_WB, args, None, descr=self.calldescr_jit_wb))
def do_getsetarrayitem(self, op, oopspec): ffitypeval = self.getvalue(op.getarg(1)) widthval = self.getvalue(op.getarg(2)) offsetval = self.getvalue(op.getarg(5)) if not ffitypeval.is_constant() or not widthval.is_constant() or not offsetval.is_constant(): return [op] ffitypeaddr = ffitypeval.box.getaddr() ffitype = llmemory.cast_adr_to_ptr(ffitypeaddr, clibffi.FFI_TYPE_P) offset = offsetval.box.getint() width = widthval.box.getint() descr = self._get_interior_descr(ffitype, width, offset) arglist = [ self.getvalue(op.getarg(3)).force_box(self.optimizer), self.getvalue(op.getarg(4)).force_box(self.optimizer), ] if oopspec == EffectInfo.OS_LIBFFI_GETARRAYITEM: opnum = rop.GETINTERIORFIELD_RAW elif oopspec == EffectInfo.OS_LIBFFI_SETARRAYITEM: opnum = rop.SETINTERIORFIELD_RAW arglist.append(self.getvalue(op.getarg(6)).force_box(self.optimizer)) else: assert False return [ ResOperation(opnum, arglist, op.result, descr=descr), ]
def test_mark_gc_roots(self): cpu = CPU(None, None) cpu.setup_once() regalloc = RegAlloc(MockAssembler(cpu, MockGcDescr(False))) regalloc.assembler.datablockwrapper = 'fakedatablockwrapper' boxes = [BoxPtr() for i in range(len(X86RegisterManager.all_regs))] longevity = {} for box in boxes: longevity[box] = (0, 1) regalloc.fm = X86FrameManager() regalloc.rm = X86RegisterManager(longevity, regalloc.fm, assembler=regalloc.assembler) regalloc.xrm = X86XMMRegisterManager(longevity, regalloc.fm, assembler=regalloc.assembler) cpu = regalloc.assembler.cpu for box in boxes: regalloc.rm.try_allocate_reg(box) TP = lltype.FuncType([], lltype.Signed) calldescr = cpu.calldescrof(TP, TP.ARGS, TP.RESULT, EffectInfo.MOST_GENERAL) regalloc.rm._check_invariants() box = boxes[0] regalloc.position = 0 regalloc.consider_call( ResOperation(rop.CALL, [box], BoxInt(), calldescr)) assert len(regalloc.assembler.movs) == 3 # mark = regalloc.get_mark_gc_roots(cpu.gc_ll_descr.gcrootmap) assert mark[0] == 'compressed' base = -WORD * FRAME_FIXED_SIZE expected = ['ebx', 'esi', 'edi', base, base - WORD, base - WORD * 2] assert dict.fromkeys(mark[1:]) == dict.fromkeys(expected)
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) # mark 'v_result' as freshly malloced self.recent_mallocs[v_result] = None
def do(self, opnum, argboxes, descr=None): v_result = execute_nonspec(self.cpu, opnum, argboxes, descr) if isinstance(v_result, Const): v_result = v_result.clonebox() self.loop.operations.append( ResOperation(opnum, argboxes, v_result, descr)) return v_result
def _really_force(self): if self.source_op is None: # this case should not occur; I only managed to get it once # in pypy-c-jit and couldn't reproduce it. The point is # that it relies on optimizefindnode.py computing exactly # the right level of specialization, and it seems that there # is still a corner case where it gets too specialized for # optimizeopt.py. Let's not crash in release-built # pypy-c-jit's. XXX find out when from pypy.rlib.debug import ll_assert ll_assert(False, "_really_force: source_op is None") raise InvalidLoop # newoperations = self.optimizer.newoperations newoperations.append(self.source_op) self.box = box = self.source_op.result # iteritems = self._fields.iteritems() if not we_are_translated(): #random order is fine, except for tests iteritems = list(iteritems) iteritems.sort(key=lambda (x, y): x.sort_key()) for ofs, value in iteritems: if value.is_null(): continue subbox = value.force_box() op = ResOperation(rop.SETFIELD_GC, [box, subbox], None, descr=ofs) newoperations.append(op) self._fields = None
def _gen_write_barrier(self, newops, v_base, v_value): args = [v_base, v_value] newops.append( ResOperation(rop.COND_CALL_GC_WB, args, None, descr=self.write_barrier_descr))
def produce_into(self, builder, r): fail_subset = builder.subset_of_intvars(r) original_intvars = builder.intvars[:] super(AbstractOvfOperation, self).produce_into(builder, r) if builder.cpu._overflow_flag: # overflow detected del builder.cpu._overflow_flag op = ResOperation(rop.GUARD_OVERFLOW, [], None) # the overflowed result should not be used any more, but can # be used on the failure path: recompute fail_subset including # the result, and then remove it from builder.intvars. fail_subset = builder.subset_of_intvars(r) builder.intvars[:] = original_intvars else: op = ResOperation(rop.GUARD_NO_OVERFLOW, [], None) op.descr = BasicFailDescr() op.fail_args = fail_subset builder.loop.operations.append(op)
def produce_into(self, builder, r): fail_subset = builder.subset_of_intvars(r) subset, f = self.non_raising_func_code(builder, r) if len(subset) == 0: RES = lltype.Void else: RES = lltype.Signed TP = lltype.FuncType([lltype.Signed] * len(subset), RES) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT) self.put(builder, args, descr) op = ResOperation(rop.GUARD_NO_EXCEPTION, [], None, descr=BasicFailDescr()) op.fail_args = fail_subset builder.loop.operations.append(op)
def produce_into(self, builder, r): subset, f, exc = self.raising_func_code(builder, r) TP = lltype.FuncType([lltype.Signed] * len(subset), lltype.Void) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT) self.put(builder, args, descr) assert builder.cpu.get_exception() builder.cpu.clear_exception() op = ResOperation(rop.GUARD_NO_EXCEPTION, [], BoxPtr(), descr=builder.make_fail_descr()) op._exc_box = ConstAddr(llmemory.cast_ptr_to_adr(exc), builder.cpu) op.fail_args = builder.subset_of_intvars(r) builder.should_fail_by = op builder.guard_op = op builder.loop.operations.append(op)
def produce_into(self, builder, r): subset, f, exc = self.raising_func_code(builder, r) TP = lltype.FuncType([lltype.Signed] * len(subset), lltype.Void) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT) self.put(builder, args, descr) while True: _, vtableptr = builder.get_random_structure_type_and_vtable(r) if vtableptr != exc: break other_box = ConstAddr(llmemory.cast_ptr_to_adr(vtableptr), builder.cpu) op = ResOperation(rop.GUARD_EXCEPTION, [other_box], BoxPtr(), descr=BasicFailDescr()) op._exc_box = ConstAddr(llmemory.cast_ptr_to_adr(exc), builder.cpu) op.setfailargs(builder.subset_of_intvars(r)) builder.should_fail_by = op builder.guard_op = op builder.loop.operations.append(op)
def _really_force(self, optforce): if self.mode is mode_string: s = self.get_constant_string_spec(mode_string) if s is not None: c_s = get_const_ptr_for_string(s) self.make_constant(c_s) return else: s = self.get_constant_string_spec(mode_unicode) if s is not None: c_s = get_const_ptr_for_unicode(s) self.make_constant(c_s) return assert self.source_op is not None self.box = box = self.source_op.result lengthbox = self.getstrlen(optforce, self.mode) op = ResOperation(self.mode.NEWSTR, [lengthbox], box) if not we_are_translated(): op.name = 'FORCE' optforce.emit_operation(op) self.string_copy_parts(optforce, box, CONST_0, self.mode)
def produce_into(self, builder, r): subset, f = self.non_raising_func_code(builder, r) if len(subset) == 0: RES = lltype.Void else: RES = lltype.Signed TP = lltype.FuncType([lltype.Signed] * len(subset), RES) ptr = llhelper(lltype.Ptr(TP), f) c_addr = ConstAddr(llmemory.cast_ptr_to_adr(ptr), builder.cpu) args = [c_addr] + subset descr = builder.cpu.calldescrof(TP, TP.ARGS, TP.RESULT) self.put(builder, args, descr) _, vtableptr = builder.get_random_structure_type_and_vtable(r) exc_box = ConstAddr(llmemory.cast_ptr_to_adr(vtableptr), builder.cpu) op = ResOperation(rop.GUARD_EXCEPTION, [exc_box], BoxPtr(), descr=BasicFailDescr()) op.fail_args = builder.subset_of_intvars(r) op._exc_box = None builder.should_fail_by = op builder.guard_op = op builder.loop.operations.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: 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) elif opnum == rop.NEWUNICODE: self.handle_new_array(self.gc_ll_descr.unicode_descr, op) else: raise NotImplementedError(op.getopname())
def produce_into(self, builder, r): fail_subset = builder.subset_of_intvars(r) original_intvars = builder.intvars[:] super(AbstractOvfOperation, self).produce_into(builder, r) if builder.fakemetainterp._got_exc: # overflow detected assert isinstance(builder.fakemetainterp._got_exc, OverflowError) op = ResOperation(rop.GUARD_OVERFLOW, [], None) # the overflowed result should not be used any more, but can # be used on the failure path: recompute fail_subset including # the result, and then remove it from builder.intvars. fail_subset = builder.subset_of_intvars(r) builder.intvars[:] = original_intvars else: op = ResOperation(rop.GUARD_NO_OVERFLOW, [], None) op.setdescr(BasicFailDescr()) op.setfailargs(fail_subset) builder.loop.operations.append(op)
def parse_op_no_result(self, line): opnum, args, descr, fail_args = self.parse_op(line) res = ResOperation(opnum, args, None, descr) res.fail_args = fail_args return res
def optimize_VIRTUAL_REF(self, op): op = ResOperation(rop.SAME_AS, [op.getarg(0)], op.result) self.emit_operation(op)
def propagate_all_forward(self): loop = self.optimizer.loop jumpop = loop.operations[-1] if jumpop.getopnum() == rop.JUMP: loop.operations = loop.operations[:-1] else: loopop = None self.optimizer.propagate_all_forward() if jumpop: assert jumpop.getdescr() is loop.token jump_args = jumpop.getarglist() jumpop.initarglist([]) #virtual_state = [self.getvalue(a).is_virtual() for a in jump_args] modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(jump_args) loop.preamble.operations = self.optimizer.newoperations loop.preamble.quasi_immutable_deps = ( self.optimizer.quasi_immutable_deps) self.optimizer = self.optimizer.reconstruct_for_next_iteration() inputargs = self.inline(self.cloned_operations, loop.inputargs, jump_args) loop.inputargs = inputargs jmp = ResOperation(rop.JUMP, loop.inputargs[:], None) jmp.setdescr(loop.token) loop.preamble.operations.append(jmp) loop.operations = self.optimizer.newoperations loop.quasi_immutable_deps = self.optimizer.quasi_immutable_deps start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable() assert isinstance(start_resumedescr, ResumeGuardDescr) snapshot = start_resumedescr.rd_snapshot while snapshot is not None: snapshot_args = snapshot.boxes new_snapshot_args = [] for a in snapshot_args: if not isinstance(a, Const): a = loop.preamble.inputargs[jump_args.index(a)] new_snapshot_args.append(a) snapshot.boxes = new_snapshot_args snapshot = snapshot.prev short = self.create_short_preamble(loop.preamble, loop) if short: if False: # FIXME: This should save some memory but requires # a lot of tests to be fixed... loop.preamble.operations = short[:] # Turn guards into conditional jumps to the preamble for i in range(len(short)): op = short[i] if op.is_guard(): op = op.clone() op.setfailargs(None) op.setdescr(start_resumedescr.clone_if_mutable()) short[i] = op short_loop = TreeLoop('short preamble') short_loop.inputargs = loop.preamble.inputargs[:] short_loop.operations = short # Clone ops and boxes to get private versions and newargs = [a.clonebox() for a in short_loop.inputargs] inliner = Inliner(short_loop.inputargs, newargs) short_loop.inputargs = newargs ops = [inliner.inline_op(op) for op in short_loop.operations] short_loop.operations = ops descr = start_resumedescr.clone_if_mutable() inliner.inline_descr_inplace(descr) short_loop.start_resumedescr = descr assert isinstance(loop.preamble.token, LoopToken) if loop.preamble.token.short_preamble: loop.preamble.token.short_preamble.append(short_loop) else: loop.preamble.token.short_preamble = [short_loop] short_loop.virtual_state = virtual_state # Forget the values to allow them to be freed for box in short_loop.inputargs: box.forget_value() for op in short_loop.operations: if op.result: op.result.forget_value()
def propagate_all_forward(self): loop = self.optimizer.loop jumpop = loop.operations[-1] if jumpop.getopnum() == rop.JUMP: loop.operations = loop.operations[:-1] else: loopop = None self.optimizer.propagate_all_forward() if jumpop: assert jumpop.getdescr() is loop.token jump_args = jumpop.getarglist() jumpop.initarglist([]) self.optimizer.flush() KillHugeIntBounds(self.optimizer).apply() loop.preamble.operations = self.optimizer.newoperations jump_args = [self.getvalue(a).get_key_box() for a in jump_args] start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable() self.start_resumedescr = start_resumedescr assert isinstance(start_resumedescr, ResumeGuardDescr) start_resumedescr.rd_snapshot = self.fix_snapshot(loop, jump_args, start_resumedescr.rd_snapshot) modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(jump_args) values = [self.getvalue(arg) for arg in jump_args] inputargs = virtual_state.make_inputargs(values) short_inputargs = virtual_state.make_inputargs(values, keyboxes=True) self.constant_inputargs = {} for box in jump_args: const = self.get_constant_box(box) if const: self.constant_inputargs[box] = const sb = ShortBoxes(self.optimizer, inputargs + self.constant_inputargs.keys()) self.short_boxes = sb preamble_optimizer = self.optimizer loop.preamble.quasi_immutable_deps = self.optimizer.quasi_immutable_deps self.optimizer = self.optimizer.new() loop.quasi_immutable_deps = self.optimizer.quasi_immutable_deps logops = self.optimizer.loop.logops if logops: args = ", ".join([logops.repr_of_arg(arg) for arg in inputargs]) debug_print("inputargs: " + args) args = ", ".join([logops.repr_of_arg(arg) for arg in short_inputargs]) debug_print("short inputargs: " + args) self.short_boxes.debug_print(logops) # Force virtuals amoung the jump_args of the preamble to get the # operations needed to setup the proper state of those virtuals # in the peeled loop inputarg_setup_ops = [] preamble_optimizer.newoperations = [] seen = {} for box in inputargs: if box in seen: continue seen[box] = True value = preamble_optimizer.getvalue(box) inputarg_setup_ops.extend(value.make_guards(box)) for box in short_inputargs: if box in seen: continue seen[box] = True value = preamble_optimizer.getvalue(box) value.force_box() preamble_optimizer.flush() inputarg_setup_ops += preamble_optimizer.newoperations # Setup the state of the new optimizer by emiting the # short preamble operations and discarding the result self.optimizer.emitting_dissabled = True for op in inputarg_setup_ops: self.optimizer.send_extra_operation(op) seen = {} for op in self.short_boxes.operations(): self.ensure_short_op_emitted(op, self.optimizer, seen) if op and op.result: # The order of these guards is not important as # self.optimizer.emitting_dissabled is False value = preamble_optimizer.getvalue(op.result) for guard in value.make_guards(op.result): self.optimizer.send_extra_operation(guard) newresult = self.optimizer.getvalue(op.result).get_key_box() if newresult is not op.result: self.short_boxes.alias(newresult, op.result) self.optimizer.flush() self.optimizer.emitting_dissabled = False # XXX Hack to prevent the arraylen/strlen/unicodelen ops generated # by value.make_guards() from ending up in pure_operations for key, op in self.optimizer.pure_operations.items(): if not self.short_boxes.has_producer(op.result): del self.optimizer.pure_operations[key] initial_inputargs_len = len(inputargs) self.inliner = Inliner(loop.inputargs, jump_args) short = self.inline(inputargs, self.cloned_operations, loop.inputargs, short_inputargs, virtual_state) loop.inputargs = inputargs args = [preamble_optimizer.getvalue(self.short_boxes.original(a)).force_box() for a in inputargs] jmp = ResOperation(rop.JUMP, args, None) jmp.setdescr(loop.token) loop.preamble.operations.append(jmp) loop.operations = self.optimizer.newoperations maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards if self.optimizer.emitted_guards > maxguards: loop.preamble.token.retraced_count = sys.maxint if short: assert short[-1].getopnum() == rop.JUMP short[-1].setdescr(loop.token) # Turn guards into conditional jumps to the preamble for i in range(len(short)): op = short[i] if op.is_guard(): op = op.clone() op.setfailargs(None) descr = self.start_resumedescr.clone_if_mutable() op.setdescr(descr) short[i] = op short_loop = TreeLoop("short preamble") short_loop.inputargs = short_inputargs short_loop.operations = short # Clone ops and boxes to get private versions and boxmap = {} newargs = [None] * len(short_loop.inputargs) for i in range(len(short_loop.inputargs)): a = short_loop.inputargs[i] if a in boxmap: newargs[i] = boxmap[a] else: newargs[i] = a.clonebox() boxmap[a] = newargs[i] inliner = Inliner(short_loop.inputargs, newargs) for box, const in self.constant_inputargs.items(): inliner.argmap[box] = const short_loop.inputargs = newargs ops = [inliner.inline_op(op) for op in short_loop.operations] short_loop.operations = ops descr = self.start_resumedescr.clone_if_mutable() inliner.inline_descr_inplace(descr) short_loop.start_resumedescr = descr assert isinstance(loop.preamble.token, LoopToken) if loop.preamble.token.short_preamble: loop.preamble.token.short_preamble.append(short_loop) else: loop.preamble.token.short_preamble = [short_loop] short_loop.virtual_state = virtual_state # Forget the values to allow them to be freed for box in short_loop.inputargs: box.forget_value() for op in short_loop.operations: if op.result: op.result.forget_value()
def propagate_all_forward(self): loop = self.optimizer.loop self.optimizer.clear_newoperations() start_label = loop.operations[0] if start_label.getopnum() == rop.LABEL: loop.operations = loop.operations[1:] # We need to emit the label op before import_state() as emitting it # will clear heap caches self.optimizer.send_extra_operation(start_label) else: start_label = None jumpop = loop.operations[-1] if jumpop.getopnum() == rop.JUMP or jumpop.getopnum() == rop.LABEL: loop.operations = loop.operations[:-1] else: jumpop = None self.import_state(start_label) self.optimizer.propagate_all_forward(clear=False) if not jumpop: return cell_token = jumpop.getdescr() assert isinstance(cell_token, JitCellToken) stop_label = ResOperation(rop.LABEL, jumpop.getarglist(), None, TargetToken(cell_token)) if jumpop.getopnum() == rop.JUMP: if self.jump_to_already_compiled_trace(jumpop): # Found a compiled trace to jump to if self.short: # Construct our short preamble assert start_label self.close_bridge(start_label) return if start_label and self.jump_to_start_label(start_label, stop_label): # Initial label matches, jump to it jumpop = ResOperation(rop.JUMP, stop_label.getarglist(), None, descr=start_label.getdescr()) if self.short: # Construct our short preamble self.close_loop(start_label, jumpop) else: self.optimizer.send_extra_operation(jumpop) return if cell_token.target_tokens: limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit if cell_token.retraced_count < limit: cell_token.retraced_count += 1 debug_print('Retracing (%d/%d)' % (cell_token.retraced_count, limit)) else: debug_print("Retrace count reached, jumping to preamble") assert cell_token.target_tokens[0].virtual_state is None jumpop.setdescr(cell_token.target_tokens[0]) self.optimizer.send_extra_operation(jumpop) return # Found nothing to jump to, emit a label instead if self.short: # Construct our short preamble assert start_label self.close_bridge(start_label) self.optimizer.flush() KillHugeIntBounds(self.optimizer).apply() loop.operations = self.optimizer.get_newoperations() self.export_state(stop_label) loop.operations.append(stop_label)
def create_short_preamble(self, preamble, loop): #return None # Dissable preamble_ops = preamble.operations loop_ops = loop.operations boxmap = BoxMap() state = ExeState(self.optimizer) short_preamble = [] loop_i = preamble_i = 0 while preamble_i < len(preamble_ops): op = preamble_ops[preamble_i] try: newop = self.inliner.inline_op(op, ignore_result=True, ignore_failargs=True) except KeyError: debug_print("create_short_preamble failed due to", "new boxes created during optimization.", "op:", op.getopnum(), "at preamble position: ", preamble_i, "loop position: ", loop_i) return None if self.sameop(newop, loop_ops[loop_i]) \ and loop_i < len(loop_ops): try: boxmap.link_ops(op, loop_ops[loop_i]) except ImpossibleLink: debug_print("create_short_preamble failed due to", "impossible link of " "op:", op.getopnum(), "at preamble position: ", preamble_i, "loop position: ", loop_i) return None loop_i += 1 else: if not state.safe_to_move(op): debug_print("create_short_preamble failed due to", "unsafe op:", op.getopnum(), "at preamble position: ", preamble_i, "loop position: ", loop_i) return None short_preamble.append(op) state.update(op) preamble_i += 1 if loop_i < len(loop_ops): debug_print("create_short_preamble failed due to", "loop contaning ops not in preamble" "at position", loop_i) return None jumpargs = [] for i in range(len(loop.inputargs)): try: jumpargs.append(boxmap.get_preamblebox(loop.inputargs[i])) except KeyError: debug_print("create_short_preamble failed due to", "input arguments not located") return None jmp = ResOperation(rop.JUMP, jumpargs[:], None) jmp.setdescr(loop.token) short_preamble.append(jmp) # Check that boxes used as arguemts are produced. seen = {} for box in preamble.inputargs: seen[box] = True for op in short_preamble: for box in op.getarglist(): if isinstance(box, Const): continue if box not in seen: debug_print("create_short_preamble failed due to", "op arguments not produced") return None if op.result: seen[op.result] = True return short_preamble