-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
Description
Mono mini has a bunch of box related IL instruction sequence optimizations. See the following code
runtime/src/mono/mono/mini/method-to-ir.c
Lines 9603 to 9882 in 5ade1ee
| /* | |
| * Look for: | |
| * | |
| * <push int/long ptr> | |
| * <push int/long> | |
| * box MyFlags | |
| * constrained. MyFlags | |
| * callvirt instance bool class [mscorlib] System.Enum::HasFlag (class [mscorlib] System.Enum) | |
| * | |
| * If we find this sequence and the operand types on box and constrained | |
| * are equal, we can emit a specialized instruction sequence instead of | |
| * the very slow HasFlag () call. | |
| * This code sequence is generated by older mcs/csc, the newer one is handled in | |
| * emit_inst_for_method (). | |
| */ | |
| guint32 constrained_token; | |
| guint32 callvirt_token; | |
| if ((cfg->opt & MONO_OPT_INTRINS) && | |
| // FIXME ip_in_bb as we go? | |
| next_ip < end && ip_in_bb (cfg, cfg->cbb, next_ip) && | |
| (ip = il_read_constrained (next_ip, end, &constrained_token)) && | |
| ip_in_bb (cfg, cfg->cbb, ip) && | |
| (ip = il_read_callvirt (ip, end, &callvirt_token)) && | |
| ip_in_bb (cfg, cfg->cbb, ip) && | |
| m_class_is_enumtype (klass) && | |
| (enum_class = mini_get_class (method, constrained_token, generic_context)) && | |
| (has_flag = mini_get_method (cfg, method, callvirt_token, NULL, generic_context)) && | |
| has_flag->klass == mono_defaults.enum_class && | |
| !strcmp (has_flag->name, "HasFlag") && | |
| (has_flag_sig = mono_method_signature_internal (has_flag)) && | |
| has_flag_sig->hasthis && | |
| has_flag_sig->param_count == 1) { | |
| CHECK_TYPELOAD (enum_class); | |
| if (enum_class == klass) { | |
| MonoInst *enum_this, *enum_flag; | |
| next_ip = ip; | |
| il_op = MONO_CEE_CALLVIRT; | |
| --sp; | |
| enum_this = sp [0]; | |
| enum_flag = sp [1]; | |
| *sp++ = mini_handle_enum_has_flag (cfg, klass, enum_this, -1, enum_flag); | |
| break; | |
| } | |
| } | |
| guint32 unbox_any_token; | |
| /* | |
| * Common in generic code: | |
| * box T1, unbox.any T2. | |
| */ | |
| if ((cfg->opt & MONO_OPT_INTRINS) && | |
| next_ip < end && ip_in_bb (cfg, cfg->cbb, next_ip) && | |
| (ip = il_read_unbox_any (next_ip, end, &unbox_any_token))) { | |
| MonoClass *unbox_klass = mini_get_class (method, unbox_any_token, generic_context); | |
| CHECK_TYPELOAD (unbox_klass); | |
| if (klass == unbox_klass) { | |
| next_ip = ip; | |
| *sp++ = val; | |
| break; | |
| } | |
| } | |
| // Optimize | |
| // | |
| // box | |
| // call object::GetType() | |
| // | |
| guint32 gettype_token; | |
| if ((ip = il_read_call(next_ip, end, &gettype_token)) && ip_in_bb (cfg, cfg->cbb, ip)) { | |
| MonoMethod* gettype_method = mini_get_method (cfg, method, gettype_token, NULL, generic_context); | |
| if (!strcmp (gettype_method->name, "GetType") && gettype_method->klass == mono_defaults.object_class) { | |
| mono_class_init_internal(klass); | |
| if (mono_class_get_checked (m_class_get_image (klass), m_class_get_type_token (klass), error) == klass) { | |
| if (cfg->compile_aot) { | |
| EMIT_NEW_TYPE_FROM_HANDLE_CONST (cfg, ins, m_class_get_image (klass), m_class_get_type_token (klass), generic_context); | |
| } else { | |
| MonoType *klass_type = m_class_get_byval_arg (klass); | |
| MonoReflectionType* reflection_type = mono_type_get_object_checked (klass_type, cfg->error); | |
| EMIT_NEW_PCONST (cfg, ins, reflection_type); | |
| } | |
| ins->type = STACK_OBJ; | |
| ins->klass = mono_defaults.systemtype_class; | |
| *sp++ = ins; | |
| next_ip = ip; | |
| break; | |
| } | |
| } | |
| } | |
| // Optimize | |
| // | |
| // box | |
| // ldnull | |
| // ceq (or cgt.un) | |
| // | |
| // to just | |
| // | |
| // ldc.i4.0 (or 1) | |
| guchar* ldnull_ip; | |
| if ((ldnull_ip = il_read_op (next_ip, end, CEE_LDNULL, MONO_CEE_LDNULL)) && ip_in_bb (cfg, cfg->cbb, ldnull_ip)) { | |
| gboolean is_eq = FALSE, is_neq = FALSE; | |
| if ((ip = il_read_op (ldnull_ip, end, CEE_PREFIX1, MONO_CEE_CEQ))) | |
| is_eq = TRUE; | |
| else if ((ip = il_read_op (ldnull_ip, end, CEE_PREFIX1, MONO_CEE_CGT_UN))) | |
| is_neq = TRUE; | |
| if ((is_eq || is_neq) && ip_in_bb (cfg, cfg->cbb, ip) && | |
| !mono_class_is_nullable (klass) && !mini_is_gsharedvt_klass (klass)) { | |
| next_ip = ip; | |
| il_op = (MonoOpcodeEnum) (is_eq ? CEE_LDC_I4_0 : CEE_LDC_I4_1); | |
| EMIT_NEW_ICONST (cfg, ins, is_eq ? 0 : 1); | |
| ins->type = STACK_I4; | |
| *sp++ = ins; | |
| break; | |
| } | |
| } | |
| guint32 isinst_tk = 0; | |
| if ((ip = il_read_op_and_token (next_ip, end, CEE_ISINST, MONO_CEE_ISINST, &isinst_tk)) && | |
| ip_in_bb (cfg, cfg->cbb, ip)) { | |
| MonoClass *isinst_class = mini_get_class (method, isinst_tk, generic_context); | |
| if (!mono_class_is_nullable (klass) && !mono_class_is_nullable (isinst_class) && | |
| !mini_is_gsharedvt_variable_klass (klass) && !mini_is_gsharedvt_variable_klass (isinst_class) && | |
| !mono_class_is_open_constructed_type (m_class_get_byval_arg (klass)) && | |
| !mono_class_is_open_constructed_type (m_class_get_byval_arg (isinst_class))) { | |
| // Optimize | |
| // | |
| // box | |
| // isinst [Type] | |
| // brfalse/brtrue | |
| // | |
| // to | |
| // | |
| // ldc.i4.0 (or 1) | |
| // brfalse/brtrue | |
| // | |
| guchar* br_ip = NULL; | |
| if ((br_ip = il_read_brtrue (ip, end, &target)) || (br_ip = il_read_brtrue_s (ip, end, &target)) || | |
| (br_ip = il_read_brfalse (ip, end, &target)) || (br_ip = il_read_brfalse_s (ip, end, &target))) { | |
| gboolean isinst = mono_class_is_assignable_from_internal (isinst_class, klass); | |
| next_ip = ip; | |
| il_op = (MonoOpcodeEnum) (isinst ? CEE_LDC_I4_1 : CEE_LDC_I4_0); | |
| EMIT_NEW_ICONST (cfg, ins, isinst ? 1 : 0); | |
| ins->type = STACK_I4; | |
| *sp++ = ins; | |
| break; | |
| } | |
| // Optimize | |
| // | |
| // box | |
| // isinst [Type] | |
| // ldnull | |
| // ceq/cgt.un | |
| // | |
| // to | |
| // | |
| // ldc.i4.0 (or 1) | |
| // | |
| ldnull_ip = NULL; | |
| if ((ldnull_ip = il_read_op (ip, end, CEE_LDNULL, MONO_CEE_LDNULL)) && ip_in_bb (cfg, cfg->cbb, ldnull_ip)) { | |
| gboolean is_eq = FALSE, is_neq = FALSE; | |
| if ((ip = il_read_op (ldnull_ip, end, CEE_PREFIX1, MONO_CEE_CEQ))) | |
| is_eq = TRUE; | |
| else if ((ip = il_read_op (ldnull_ip, end, CEE_PREFIX1, MONO_CEE_CGT_UN))) | |
| is_neq = TRUE; | |
| if ((is_eq || is_neq) && ip_in_bb (cfg, cfg->cbb, ip) && | |
| !mono_class_is_nullable (klass) && !mini_is_gsharedvt_klass (klass)) { | |
| gboolean isinst = mono_class_is_assignable_from_internal (isinst_class, klass); | |
| next_ip = ip; | |
| if (is_eq) | |
| isinst = !isinst; | |
| il_op = (MonoOpcodeEnum) (isinst ? CEE_LDC_I4_1 : CEE_LDC_I4_0); | |
| EMIT_NEW_ICONST (cfg, ins, isinst ? 1 : 0); | |
| ins->type = STACK_I4; | |
| *sp++ = ins; | |
| break; | |
| } | |
| } | |
| // Optimize | |
| // | |
| // box | |
| // isinst [Type] | |
| // unbox.any | |
| // | |
| // to | |
| // | |
| // nop | |
| // | |
| guchar* unbox_ip = NULL; | |
| guint32 unbox_token = 0; | |
| if ((unbox_ip = il_read_unbox_any (ip, end, &unbox_token)) && ip_in_bb (cfg, cfg->cbb, unbox_ip)) { | |
| MonoClass *unbox_klass = mini_get_class (method, unbox_token, generic_context); | |
| CHECK_TYPELOAD (unbox_klass); | |
| if (!mono_class_is_nullable (unbox_klass) && | |
| !mini_is_gsharedvt_klass (unbox_klass) && | |
| klass == isinst_class && | |
| klass == unbox_klass) | |
| { | |
| *sp++ = val; | |
| next_ip = unbox_ip; | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| // ASSUME | |
| // interface ISomeIface { void Method(); } | |
| // struct SomeStruct : ISomeIface {...} | |
| // OPTIMIZE | |
| // box SomeStruct | |
| // callvirt instance ISomeIface::Method() | |
| // TO | |
| // call SomeStruct::Method() | |
| guint32 callvirt_proc_token; | |
| if (!((cfg->compile_aot || cfg->compile_llvm) && !mono_class_is_def(klass)) && // we cannot devirtualize in AOT when using generics | |
| next_ip < end && | |
| il_read_callvirt (next_ip, end, &callvirt_proc_token) && | |
| ip_in_bb (cfg, cfg->cbb, next_ip) ) { | |
| MonoMethod* iface_method; | |
| MonoMethodSignature* iface_method_sig; | |
| if (val && | |
| val->flags != MONO_INST_FAULT && // not null | |
| !mono_class_is_nullable (klass) && | |
| !mini_is_gsharedvt_klass (klass) && | |
| (iface_method = mini_get_method (cfg, method, callvirt_proc_token, NULL, generic_context)) && | |
| (iface_method_sig = mono_method_signature_internal (iface_method)) && // callee signture is healthy | |
| iface_method_sig->hasthis && | |
| iface_method_sig->param_count == 0 && // the callee has no args (other than this) | |
| !iface_method_sig->has_type_parameters && | |
| iface_method_sig->generic_param_count == 0) { // and no type params, apparently virtual generic methods require special handling | |
| if (!m_class_is_inited (iface_method->klass)) { | |
| if (!mono_class_init_internal (iface_method->klass)) | |
| TYPE_LOAD_ERROR (iface_method->klass); | |
| } | |
| ERROR_DECL (struct_method_error); | |
| MonoMethod* struct_method = mono_class_get_virtual_method (klass, iface_method, struct_method_error); | |
| if (is_ok (struct_method_error)) { | |
| MonoMethodSignature* struct_method_sig = mono_method_signature_internal (struct_method); | |
| if (!struct_method || | |
| !MONO_METHOD_IS_FINAL (struct_method) || | |
| !struct_method_sig || | |
| struct_method_sig->has_type_parameters || | |
| !mono_method_can_access_method (method, struct_method)) { | |
| // do not optimize, let full callvirt deal with it | |
| } else if (val->opcode == OP_TYPED_OBJREF) { | |
| *sp++ = val; | |
| cmethod_override = struct_method; | |
| break; | |
| } else { | |
| MonoInst* srcvar = get_vreg_to_inst (cfg, val->dreg); | |
| if (!srcvar) | |
| srcvar = mono_compile_create_var_for_vreg (cfg, m_class_get_byval_arg (klass), OP_LOCAL, val->dreg); | |
| EMIT_NEW_VARLOADA (cfg, ins, srcvar, m_class_get_byval_arg (klass)); | |
| *sp++= ins; | |
| cmethod_override = struct_method; | |
| break; | |
| } | |
| } else { | |
| mono_error_cleanup (struct_method_error); | |
| } | |
| } | |
| } |
Interpreter needs to support these optimizations as well, in order to support the ref struct feature.