class Method(object): operations = [] # overwritten at the end of the module debug = False tailcall = True nocast = True def __init__(self, cpu, cliloop): self.setoptions() self.cpu = cpu self.name = cliloop.get_fresh_cli_name() self.cliloop = cliloop self.boxes = {} # box --> local var self.branches = [] self.branchlabels = [] self.consts = {} # object --> index self.meth_wrapper = self._get_meth_wrapper() self.il = self.meth_wrapper.get_il_generator() self.av_consts = MethodArgument(0, System.Type.GetType("System.Object[]")) t_InputArgs = dotnet.typeof(InputArgs) self.av_inputargs = MethodArgument(1,t_InputArgs ) self.av_ovf_flag = BoxInt() self.exc_value_field = t_InputArgs.GetField('exc_value') if cpu.rtyper: self.av_OverflowError = ConstObj(ootype.cast_to_object(cpu.ll_ovf_exc)) self.av_ZeroDivisionError = ConstObj(ootype.cast_to_object(cpu.ll_zero_exc)) else: self.av_OverflowError = None self.av_ZeroDivisionError = None self.box2type = {} def compile(self): # ---- debug_start('jit-backend-emit_ops') if self.nocast: self.compute_types() self.emit_load_inputargs() self.emit_preamble() self.emit_operations(self.cliloop.operations) self.emit_branches() self.emit_end() debug_stop('jit-backend-emit_ops') # ---- debug_start('jit-backend-finish_code') res = self.finish_code() debug_stop('jit-backend-finish_code') return res def _parseopt(self, text): text = text.lower() if text[0] == '-': return text[1:], False elif text[0] == '+': return text[1:], True else: return text, True def setoptions(self): opts = os.environ.get('PYPYJITOPT') if not opts: return parts = opts.split(' ') for part in parts: name, value = self._parseopt(part) if name == 'debug': self.debug = value elif name == 'tailcall': self.tailcall = value elif name == 'nocast': self.nocast = value else: os.write(2, 'Warning: invalid option name: %s\n' % name) def _collect_types(self, operations, box2classes): for op in operations: if op.opnum in (rop.GETFIELD_GC, rop.SETFIELD_GC): box = op.args[0] descr = op.descr assert isinstance(descr, runner.FieldDescr) box2classes.setdefault(box, []).append(descr.selfclass) if op in self.cliloop.guard2ops: _, suboperations = self.cliloop.guard2ops[op] self._collect_types(suboperations, box2classes) def compute_types(self): box2classes = {} # box --> [ootype.Class] self._collect_types(self.cliloop.operations, box2classes) for box, classes in box2classes.iteritems(): cls = classes[0] for cls2 in classes[1:]: if ootype.subclassof(cls, cls2): cls = cls2 else: assert ootype.subclassof(cls2, cls) self.box2type[box] = dotnet.class2type(cls) def finish_code(self): delegatetype = dotnet.typeof(LoopDelegate) # initialize the array of genconsts consts = dotnet.new_array(System.Object, len(self.consts)) for av_const, i in self.consts.iteritems(): consts[i] = av_const.get_cliobj() # build the delegate func = self.meth_wrapper.create_delegate(delegatetype, consts) return dotnet.clidowncast(func, LoopDelegate) def _get_meth_wrapper(self): restype = dotnet.class2type(cVoid) args = self._get_args_array([dotnet.typeof(InputArgs)]) return get_method_wrapper(self.name, restype, args) def _get_args_array(self, arglist): array = dotnet.new_array(System.Type, len(arglist)+1) array[0] = System.Type.GetType("System.Object[]") for i in range(len(arglist)): array[i+1] = arglist[i] return array def var_for_box(self, box): try: return self.boxes[box] except KeyError: v = self.il.DeclareLocal(box.getCliType(self)) self.boxes[box] = v return v def match_var_fox_boxes(self, failargs, inputargs): failargs = [arg for arg in failargs if arg is not None] assert len(failargs) == len(inputargs) for i in range(len(failargs)): v = self.boxes[failargs[i]] self.boxes[inputargs[i]] = v def get_index_for_failing_op(self, op): try: return self.cpu.failing_ops.index(op) except ValueError: self.cpu.failing_ops.append(op) return len(self.cpu.failing_ops)-1 def get_index_for_constant(self, obj): try: return self.consts[obj] except KeyError: index = len(self.consts) self.consts[obj] = index return index def newbranch(self, op): # sanity check, maybe we can remove it later for myop in self.branches: assert myop is not op il_label = self.il.DefineLabel() self.branches.append(op) self.branchlabels.append(il_label) return il_label def get_inputarg_field(self, type): t = dotnet.typeof(InputArgs) if type == history.INT: fieldname = 'ints' elif type == history.FLOAT: fieldname = 'floats' elif type == history.REF: fieldname = 'objs' else: assert False, 'Unknown type %s' % type return t.GetField(fieldname) def load_inputarg(self, i, type, clitype): field = self.get_inputarg_field(type) self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldfld, field) self.il.Emit(OpCodes.Ldc_I4, i) self.il.Emit(OpCodes.Ldelem, clitype) def store_inputarg(self, i, type, clitype, valuebox): field = self.get_inputarg_field(type) self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldfld, field) self.il.Emit(OpCodes.Ldc_I4, i) valuebox.load(self) self.il.Emit(OpCodes.Stelem, clitype) def emit_load_inputargs(self): self.emit_debug("executing: " + self.name) i = 0 for box in self.cliloop.inputargs: self.load_inputarg(i, box.type, box.getCliType(self)) box.store(self) i+=1 def emit_preamble(self): self.il_loop_start = self.il.DefineLabel() self.il.MarkLabel(self.il_loop_start) def emit_operations(self, oplist): self.i = 0 self.oplist = oplist N = len(oplist) while self.i < N: op = oplist[self.i] self.emit_debug(op.repr()) func = self.operations[op.opnum] assert func is not None func(self, op) self.i += 1 def emit_branches(self): while self.branches: branches = self.branches branchlabels = self.branchlabels self.branches = [] self.branchlabels = [] assert len(branches) == len(branchlabels) for i in range(len(branches)): op = branches[i] il_label = branchlabels[i] self.il.MarkLabel(il_label) self.emit_guard_subops(op) def emit_guard_subops(self, op): assert op.is_guard() if op in self.cliloop.guard2ops: inputargs, suboperations = self.cliloop.guard2ops[op] self.match_var_fox_boxes(op.fail_args, inputargs) self.emit_operations(suboperations) else: self.emit_return_failed_op(op, op.fail_args) def emit_end(self): assert self.branches == [] self.il.Emit(OpCodes.Ret) # ----------------------------- def push_all_args(self, op): for box in op.args: box.load(self) def push_arg(self, op, n): op.args[n].load(self) def store_result(self, op): op.result.store(self) def emit_debug(self, msg): if self.debug: self.il.EmitWriteLine(msg) def emit_clear_exception(self): self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldnull) self.il.Emit(OpCodes.Stfld, self.exc_value_field) # clear the overflow flag self.il.Emit(OpCodes.Ldc_I4_0) self.av_ovf_flag.store(self) def emit_raising_op(self, op, emit_op, exctypes): self.emit_clear_exception() lbl = self.il.BeginExceptionBlock() emit_op(self, op) self.il.Emit(OpCodes.Leave, lbl) for exctype in exctypes: v = self.il.DeclareLocal(exctype) self.il.BeginCatchBlock(exctype) if exctype == dotnet.typeof(System.OverflowException) and self.av_OverflowError: self.il.Emit(OpCodes.Ldc_I4_1) self.av_ovf_flag.store(self) else: self.il.Emit(OpCodes.Stloc, v) self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldloc, v) self.il.Emit(OpCodes.Stfld, self.exc_value_field) self.il.EndExceptionBlock() def emit_ovf_op(self, op, emit_op): next_op = self.oplist[self.i+1] if next_op.opnum == rop.GUARD_NO_OVERFLOW: self.i += 1 self.emit_ovf_op_and_guard(op, next_op, emit_op) return # clear the overflow flag self.il.Emit(OpCodes.Ldc_I4_0) self.av_ovf_flag.store(self) lbl = self.il.BeginExceptionBlock() emit_op(self, op) self.il.Emit(OpCodes.Leave, lbl) self.il.BeginCatchBlock(dotnet.typeof(System.OverflowException)) self.il.Emit(OpCodes.Ldc_I4_1) self.av_ovf_flag.store(self) self.il.EndExceptionBlock() def emit_ovf_op_and_guard(self, op, opguard, emit_op): # emit the checked operation lbl = self.il.BeginExceptionBlock() emit_op(self, op) self.il.Emit(OpCodes.Leave, lbl) self.il.BeginCatchBlock(dotnet.typeof(System.OverflowException)) # emit the guard assert len(opguard.args) == 0 il_label = self.newbranch(opguard) self.il.Emit(OpCodes.Leave, il_label) self.il.EndExceptionBlock() def mark(self, msg): self.il.Emit(OpCodes.Ldstr, msg) self.il.Emit(OpCodes.Pop) # -------------------------------- def emit_return_failed_op(self, op, args): # store the index of the failed op index_op = self.get_index_for_failing_op(op) self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldc_I4, index_op) field = dotnet.typeof(InputArgs).GetField('failed_op') self.il.Emit(OpCodes.Stfld, field) self.emit_store_opargs(args) self.il.Emit(OpCodes.Ret) def emit_op_finish(self, op): self.emit_return_failed_op(op, op.args) def emit_store_opargs(self, args): # store the latest values i = 0 for box in args: if box is not None: self.store_inputarg(i, box.type, box.getCliType(self), box) i+=1 def emit_guard_bool(self, op, opcode): assert len(op.args) == 1 il_label = self.newbranch(op) op.args[0].load(self) self.il.Emit(opcode, il_label) def emit_op_guard_true(self, op): self.emit_guard_bool(op, OpCodes.Brfalse) def emit_op_guard_false(self, op): self.emit_guard_bool(op, OpCodes.Brtrue) def emit_op_guard_nonnull(self, op): self.emit_guard_bool(op, OpCodes.Brfalse) def emit_op_guard_isnull(self, op): self.emit_guard_bool(op, OpCodes.Brtrue) def emit_op_guard_value(self, op): assert len(op.args) == 2 il_label = self.newbranch(op) self.push_all_args(op) self.il.Emit(OpCodes.Bne_Un, il_label) def emit_op_guard_class(self, op): assert len(op.args) == 2 il_label = self.newbranch(op) self.push_arg(op, 0) meth = dotnet.typeof(System.Object).GetMethod("GetType") self.il.Emit(OpCodes.Callvirt, meth) self.push_arg(op, 1) self.il.Emit(OpCodes.Bne_Un, il_label) def emit_op_guard_nonnull_class(self, op): assert len(op.args) == 2 il_label = self.newbranch(op) # nonnull check self.push_arg(op, 0) self.il.Emit(OpCodes.Brfalse, il_label) # class check self.push_arg(op, 0) meth = dotnet.typeof(System.Object).GetMethod("GetType") self.il.Emit(OpCodes.Callvirt, meth) self.push_arg(op, 1) self.il.Emit(OpCodes.Bne_Un, il_label) def emit_op_guard_no_exception(self, op): il_label = self.newbranch(op) self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldfld, self.exc_value_field) self.il.Emit(OpCodes.Brtrue, il_label) def emit_op_guard_exception(self, op): il_label = self.newbranch(op) classbox = op.args[0] assert isinstance(classbox, ConstObj) oocls = classbox.getref(ootype.Class) clitype = dotnet.class2type(oocls) self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldfld, self.exc_value_field) self.il.Emit(OpCodes.Isinst, clitype) self.il.Emit(OpCodes.Brfalse, il_label) # the guard succeeded, store the result self.av_inputargs.load(self) self.il.Emit(OpCodes.Ldfld, self.exc_value_field) self.store_result(op) def emit_guard_overflow_impl(self, op, opcode): assert len(op.args) == 0 il_label = self.newbranch(op) self.av_ovf_flag.load(self) self.il.Emit(opcode, il_label) def emit_op_guard_no_overflow(self, op): self.emit_guard_overflow_impl(op, OpCodes.Brtrue) def emit_op_guard_overflow(self, op): self.emit_guard_overflow_impl(op, OpCodes.Brfalse) def emit_op_jump(self, op): target_token = op.descr assert isinstance(target_token, LoopToken) if target_token.cliloop is self.cliloop: # jump to the beginning of the loop i = 0 for i in range(len(op.args)): op.args[i].load(self) self.cliloop.inputargs[i].store(self) self.il.Emit(OpCodes.Br, self.il_loop_start) else: # it's a real bridge cliloop = target_token.cliloop assert len(op.args) == len(cliloop.inputargs) self.emit_debug('jumping to ' + cliloop.name) self.emit_store_opargs(op.args) cliloop.funcbox.load(self) self.av_inputargs.load(self) methinfo = dotnet.typeof(LoopDelegate).GetMethod('Invoke') if self.tailcall: self.il.Emit(OpCodes.Tailcall) self.il.Emit(OpCodes.Callvirt, methinfo) self.il.Emit(OpCodes.Ret) def emit_op_new_with_vtable(self, op): clsbox = op.args[0] assert isinstance(clsbox, ConstObj) cls = clsbox.getref_base() descr = self.cpu.class_sizes[cls] assert isinstance(descr, runner.TypeDescr) clitype = descr.get_clitype() ctor_info = descr.get_constructor_info() self.il.Emit(OpCodes.Newobj, ctor_info) self.store_result(op) def emit_op_runtimenew(self, op): clitype_utils = dotnet.typeof(Utils) methinfo = clitype_utils.GetMethod('RuntimeNew') op.args[0].load(self) self.il.Emit(OpCodes.Call, methinfo) self.store_result(op) def emit_op_instanceof(self, op): descr = op.descr assert isinstance(descr, runner.TypeDescr) clitype = descr.get_clitype() op.args[0].load(self) self.il.Emit(OpCodes.Isinst, clitype) self.il.Emit(OpCodes.Ldnull) self.il.Emit(OpCodes.Cgt_Un) self.store_result(op) def emit_op_subclassof(self, op): clitype_utils = dotnet.typeof(Utils) methinfo = clitype_utils.GetMethod('SubclassOf') op.args[0].load(self) op.args[1].load(self) self.il.Emit(OpCodes.Call, methinfo) self.store_result(op) def emit_op_call_impl(self, op): descr = op.descr assert isinstance(descr, runner.StaticMethDescr) delegate_type = descr.get_delegate_clitype() meth_invoke = descr.get_meth_info() self._emit_call(op, OpCodes.Callvirt, delegate_type, meth_invoke, descr.has_result) def emit_op_call(self, op): emit_op = Method.emit_op_call_impl.im_func exctypes = [dotnet.typeof(System.Exception)] self.emit_raising_op(op, emit_op, exctypes) emit_op_call_pure = emit_op_call def emit_op_oosend(self, op): descr = op.descr assert isinstance(descr, runner.MethDescr) clitype = descr.get_self_clitype() methinfo = descr.get_meth_info() opcode = descr.get_call_opcode() self._emit_call(op, opcode, clitype, methinfo, descr.has_result) emit_op_oosend_pure = emit_op_oosend def _emit_call(self, op, opcode, clitype, methinfo, has_result): av_sm, args_av = op.args[0], op.args[1:] av_sm.load(self) self.il.Emit(OpCodes.Castclass, clitype) for av_arg in args_av: av_arg.load(self) self.il.Emit(opcode, methinfo) if has_result: self.store_result(op) def emit_op_getfield_gc(self, op): descr = op.descr assert isinstance(descr, runner.FieldDescr) clitype = descr.get_self_clitype() fieldinfo = descr.get_field_info() obj = op.args[0] obj.load(self) if obj.getCliType(self) is not clitype: self.il.Emit(OpCodes.Castclass, clitype) self.il.Emit(OpCodes.Ldfld, fieldinfo) self.store_result(op) emit_op_getfield_gc_pure = emit_op_getfield_gc def emit_op_setfield_gc(self, op): descr = op.descr assert isinstance(descr, runner.FieldDescr) clitype = descr.get_self_clitype() fieldinfo = descr.get_field_info() obj = op.args[0] obj.load(self) if obj.getCliType(self) is not clitype: self.il.Emit(OpCodes.Castclass, clitype) op.args[1].load(self) self.il.Emit(OpCodes.Stfld, fieldinfo) def emit_op_getarrayitem_gc(self, op): descr = op.descr assert isinstance(descr, runner.TypeDescr) clitype = descr.get_array_clitype() itemtype = descr.get_clitype() op.args[0].load(self) self.il.Emit(OpCodes.Castclass, clitype) op.args[1].load(self) self.il.Emit(OpCodes.Ldelem, itemtype) self.store_result(op) emit_op_getarrayitem_gc_pure = emit_op_getarrayitem_gc def emit_op_setarrayitem_gc(self, op): descr = op.descr assert isinstance(descr, runner.TypeDescr) clitype = descr.get_array_clitype() itemtype = descr.get_clitype() op.args[0].load(self) self.il.Emit(OpCodes.Castclass, clitype) op.args[1].load(self) op.args[2].load(self) self.il.Emit(OpCodes.Stelem, itemtype) def emit_op_arraylen_gc(self, op): descr = op.descr assert isinstance(descr, runner.TypeDescr) clitype = descr.get_array_clitype() op.args[0].load(self) self.il.Emit(OpCodes.Castclass, clitype) self.il.Emit(OpCodes.Ldlen) self.store_result(op) def emit_op_new_array(self, op): descr = op.descr assert isinstance(descr, runner.TypeDescr) item_clitype = descr.get_clitype() if item_clitype is None: return self.emit_new_arrayofvoids(op) op.args[0].load(self) self.il.Emit(OpCodes.Newarr, item_clitype) self.store_result(op) def emit_new_arrayofvoids(self, op): clitype = dotnet.typeof(ListOfVoid) ctor = clitype.GetConstructor(dotnet.new_array(System.Type, 0)) _ll_resize = clitype.GetMethod('_ll_resize') self.il.Emit(OpCodes.Newobj, ctor) self.il.Emit(OpCodes.Dup) op.args[0].load(self) self.il.Emit(OpCodes.Callvirt, _ll_resize) self.store_result(op) def emit_op_debug_merge_point(self, op): pass def lltype_only(self, op): print 'Operation %s is lltype specific, should not get here!' % op.getopname() raise NotImplementedError emit_op_new = lltype_only emit_op_setfield_raw = lltype_only emit_op_getfield_raw = lltype_only emit_op_getfield_raw_pure = lltype_only emit_op_strsetitem = lltype_only emit_op_unicodesetitem = lltype_only emit_op_cast_int_to_ptr = lltype_only emit_op_cast_ptr_to_int = lltype_only emit_op_newstr = lltype_only emit_op_strlen = lltype_only emit_op_strgetitem = lltype_only emit_op_newunicode = lltype_only emit_op_unicodelen = lltype_only emit_op_unicodegetitem = lltype_only emit_op_cond_call_gc_wb = lltype_only emit_op_setarrayitem_raw = lltype_only