diff --git a/src/mono/mono/mini/aot-compiler.c b/src/mono/mono/mini/aot-compiler.c index cd1b153379d9d2..4d4d64014c246e 100644 --- a/src/mono/mono/mini/aot-compiler.c +++ b/src/mono/mono/mini/aot-compiler.c @@ -6925,6 +6925,9 @@ encode_patch (MonoAotCompile *acfg, MonoJumpInfo *patch_info, guint8 *buf, guint case MONO_PATCH_INFO_FIELD: encode_field_info (acfg, (MonoClassField *)template_->data, p, &p); break; + case MONO_PATCH_INFO_METHOD: + encode_method_ref (acfg, (MonoMethod*)template_->data, p, &p); + break; default: g_assert_not_reached (); break; diff --git a/src/mono/mono/mini/aot-runtime.c b/src/mono/mono/mini/aot-runtime.c index a04e272b9aac18..d4de90f4b64df5 100644 --- a/src/mono/mono/mini/aot-runtime.c +++ b/src/mono/mono/mini/aot-runtime.c @@ -3906,6 +3906,12 @@ decode_patch (MonoAotModule *aot_module, MonoMemPool *mp, MonoJumpInfo *ji, guin if (!template_->data) goto cleanup; break; + case MONO_PATCH_INFO_METHOD: + template_->data = decode_resolve_method_ref (aot_module, p, &p, error); + mono_error_cleanup (error); /* FIXME don't swallow the error */ + if (!template_->data) + goto cleanup; + break; default: g_assert_not_reached (); break; diff --git a/src/mono/mono/mini/calls.c b/src/mono/mono/mini/calls.c index 8424a484aa3a9c..91921aa2edea67 100644 --- a/src/mono/mono/mini/calls.c +++ b/src/mono/mono/mini/calls.c @@ -703,7 +703,7 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth helper_sig_llvmonly_imt_trampoline = tmp; } - if (!fsig->generic_param_count && !is_iface && !is_gsharedvt) { + if (!fsig->generic_param_count && !is_iface) { /* * The simplest case, a normal virtual call. */ @@ -737,14 +737,22 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth /* Fastpath */ MONO_START_BB (cfg, non_null_bb); - /* Load the address + arg from the vtable slot */ - EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, TARGET_SIZEOF_VOID_P); - - return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); + if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) { + MonoInst *wrapper_ins = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT); + int arg_reg = alloc_preg (cfg); + EMIT_NEW_UNALU (cfg, ins, OP_MOVE, arg_reg, slot_reg); + int addr_reg = alloc_preg (cfg); + EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, wrapper_ins->dreg, 0); + return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); + } else { + /* Load the address + arg from the vtable slot */ + EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, slot_reg, 0); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, arg_reg, slot_reg, TARGET_SIZEOF_VOID_P); + return mini_emit_extra_arg_calli (cfg, fsig, sp, arg_reg, call_target); + } } - if (!fsig->generic_param_count && is_iface && !variant_iface && !is_gsharedvt && !special_array_interface) { + if (!fsig->generic_param_count && is_iface && !variant_iface && !special_array_interface) { /* * A simple interface call * @@ -780,10 +788,17 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth cmethod, MONO_RGCTX_INFO_METHOD); ftndesc_ins = mini_emit_calli (cfg, helper_sig_llvmonly_imt_trampoline, icall_args, thunk_addr_ins, NULL, NULL); - return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); + if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) { + MonoInst *wrapper_ins = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT); + int addr_reg = alloc_preg (cfg); + EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, wrapper_ins->dreg, 0); + return mini_emit_extra_arg_calli (cfg, fsig, sp, ftndesc_ins->dreg, call_target); + } else { + return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); + } } - if ((fsig->generic_param_count || variant_iface || special_array_interface) && !is_gsharedvt) { + if (fsig->generic_param_count || variant_iface || special_array_interface) { /* * This is similar to the interface case, the vtable slot points to an imt thunk which is * dynamically extended as more instantiations are discovered. @@ -847,7 +862,15 @@ mini_emit_llvmonly_virtual_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMeth /* Common case */ MONO_START_BB (cfg, end_bb); - return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); + + if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) { + MonoInst *wrapper_ins = mini_emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT); + int addr_reg = alloc_preg (cfg); + EMIT_NEW_LOAD_MEMBASE (cfg, call_target, OP_LOAD_MEMBASE, addr_reg, wrapper_ins->dreg, 0); + return mini_emit_extra_arg_calli (cfg, fsig, sp, ftndesc_ins->dreg, call_target); + } else { + return mini_emit_llvmonly_calli (cfg, fsig, sp, ftndesc_ins); + } } /* diff --git a/src/mono/mono/mini/llvmonly-runtime.c b/src/mono/mono/mini/llvmonly-runtime.c index e6979ada04f031..7da0fc0da809ff 100644 --- a/src/mono/mono/mini/llvmonly-runtime.c +++ b/src/mono/mono/mini/llvmonly-runtime.c @@ -763,6 +763,7 @@ gpointer mini_llvmonly_resolve_iface_call_gsharedvt (MonoObject *this_obj, int imt_slot, MonoMethod *imt_method, gpointer *out_arg) { ERROR_DECL (error); + gpointer res = resolve_iface_call (this_obj, imt_slot, imt_method, out_arg, TRUE, error); if (!is_ok (error)) { MonoException *ex = mono_error_convert_to_exception (error); diff --git a/src/mono/mono/mini/method-to-ir.c b/src/mono/mono/mini/method-to-ir.c index d309d441661d9b..887c4ab60799c7 100644 --- a/src/mono/mono/mini/method-to-ir.c +++ b/src/mono/mono/mini/method-to-ir.c @@ -2575,6 +2575,9 @@ mono_patch_info_rgctx_entry_new (MonoMemPool *mp, MonoMethod *method, gboolean i return res; } +static MonoInst* +emit_get_gsharedvt_info (MonoCompile *cfg, gpointer data, MonoRgctxInfoType rgctx_type); + static MonoInst* emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEntry *entry) { @@ -2584,8 +2587,8 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt EMIT_NEW_AOTCONST (cfg, slot_ins, MONO_PATCH_INFO_RGCTX_SLOT_INDEX, entry); // Can't add basic blocks during decompose/interp entry mode etc. - // FIXME: Add a fastpath for in_mrgctx - if (cfg->after_method_to_ir || cfg->gsharedvt || cfg->interp_entry_only || entry->in_mrgctx) { + // Can't add basic blocks to the gsharedvt init block either + if (cfg->after_method_to_ir || entry->info_type == MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO || cfg->interp_entry_only) { MonoInst *args [2] = { rgctx, slot_ins }; if (entry->in_mrgctx) call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args); @@ -2610,13 +2613,19 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt NEW_BBLOCK (cfg, end_bb); NEW_BBLOCK (cfg, slowpath_bb); - rgctx_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)); - // FIXME: Avoid this check by allocating the table when the vtable is created etc. - MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0); - MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb); + if (entry->in_mrgctx) { + rgctx_reg = rgctx->dreg; + } else { + rgctx_reg = alloc_preg (cfg); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, rgctx_reg, rgctx->dreg, MONO_STRUCT_OFFSET (MonoVTable, runtime_generic_context)); + // FIXME: Avoid this check by allocating the table when the vtable is created etc. + MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, rgctx_reg, 0); + MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb); + } - int table_size = mono_class_rgctx_get_array_size (0, FALSE); + int table_size = mono_class_rgctx_get_array_size (0, entry->in_mrgctx); + if (entry->in_mrgctx) + table_size -= MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT / sizeof (TARGET_SIZEOF_VOID_P); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, slot_ins->dreg, table_size - 1); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBGE, slowpath_bb); @@ -2627,7 +2636,7 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt EMIT_NEW_UNALU (cfg, ins, OP_MOVE, addr_reg, rgctx_reg); EMIT_NEW_BIALU (cfg, ins, OP_PADD, addr_reg, addr_reg, shifted_slot_reg); int val_reg = alloc_preg (cfg); - MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, addr_reg, TARGET_SIZEOF_VOID_P); + MONO_EMIT_NEW_LOAD_MEMBASE (cfg, val_reg, addr_reg, TARGET_SIZEOF_VOID_P + (entry->in_mrgctx ? MONO_SIZEOF_METHOD_RUNTIME_GENERIC_CONTEXT : 0)); MONO_EMIT_NEW_BIALU_IMM (cfg, OP_COMPARE_IMM, -1, val_reg, 0); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_PBEQ, slowpath_bb); @@ -2641,7 +2650,10 @@ emit_rgctx_fetch_inline (MonoCompile *cfg, MonoInst *rgctx, MonoJumpInfoRgctxEnt slowpath_bb->out_of_line = TRUE; MonoInst *args[2] = { rgctx, slot_ins }; - call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args); + if (entry->in_mrgctx) + call = mono_emit_jit_icall (cfg, mono_fill_method_rgctx, args); + else + call = mono_emit_jit_icall (cfg, mono_fill_class_rgctx, args); EMIT_NEW_UNALU (cfg, ins, OP_MOVE, res_reg, call->dreg); MONO_EMIT_NEW_BRANCH_BLOCK (cfg, OP_BR, end_bb); @@ -2694,6 +2706,10 @@ mini_emit_get_rgctx_klass (MonoCompile *cfg, int context_used, } } + // Its cheaper to load these from the gsharedvt info struct + if (cfg->llvm_only && cfg->gsharedvt) + return mini_emit_get_gsharedvt_info_klass (cfg, klass, rgctx_type); + MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_CLASS, klass, rgctx_type); return emit_rgctx_fetch (cfg, context_used, entry); @@ -2789,6 +2805,10 @@ emit_get_rgctx_method (MonoCompile *cfg, int context_used, g_assert_not_reached (); } } else { + // Its cheaper to load these from the gsharedvt info struct + if (cfg->llvm_only && cfg->gsharedvt) + return emit_get_gsharedvt_info (cfg, cmethod, rgctx_type); + MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_METHODCONST, cmethod, rgctx_type); return emit_rgctx_fetch (cfg, context_used, entry); @@ -2799,6 +2819,10 @@ static MonoInst* emit_get_rgctx_field (MonoCompile *cfg, int context_used, MonoClassField *field, MonoRgctxInfoType rgctx_type) { + // Its cheaper to load these from the gsharedvt info struct + if (cfg->llvm_only && cfg->gsharedvt) + return emit_get_gsharedvt_info (cfg, field, rgctx_type); + MonoJumpInfoRgctxEntry *entry = mono_patch_info_rgctx_entry_new (cfg->mempool, cfg->method, context_used_is_mrgctx (cfg, context_used), MONO_PATCH_INFO_FIELD, field, rgctx_type); return emit_rgctx_fetch (cfg, context_used, entry); diff --git a/src/mono/mono/mini/mini-generic-sharing.c b/src/mono/mono/mini/mini-generic-sharing.c index 92017081181202..9e5fa6d85a007f 100644 --- a/src/mono/mono/mini/mini-generic-sharing.c +++ b/src/mono/mono/mini/mini-generic-sharing.c @@ -564,6 +564,7 @@ inflate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoTempl case MONO_RGCTX_INFO_METHOD_FTNDESC: case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: + case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: case MONO_RGCTX_INFO_METHOD_RGCTX: case MONO_RGCTX_INFO_METHOD_CONTEXT: case MONO_RGCTX_INFO_REMOTING_INVOKE_WITH_CHECK: @@ -2231,6 +2232,17 @@ instantiate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoT return mini_llvmonly_create_ftndesc (m, addr, arg); } } + case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: { + MonoMethod *m = (MonoMethod*)data; + gpointer addr; + + /* A gsharedvt out wrapper for the signature of M */ + g_assert (mono_llvm_only); + addr = mini_get_gsharedvt_wrapper (FALSE, NULL, mono_method_signature_internal (m), NULL, -1, FALSE); + + /* Returns an ftndesc */ + return mini_llvmonly_create_ftndesc (m, addr, NULL); + } case MONO_RGCTX_INFO_VIRT_METHOD_CODE: { MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data; MonoClass *iface_class = info->method->klass; @@ -2614,6 +2626,7 @@ mono_rgctx_info_type_to_str (MonoRgctxInfoType type) case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: return "GSHAREDVT_INFO"; case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: return "GENERIC_METHOD_CODE"; case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: return "GSHAREDVT_OUT_WRAPPER"; + case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: return "GSHAREDVT_OUT_WRAPPER_VIRT"; case MONO_RGCTX_INFO_CLASS_FIELD: return "CLASS_FIELD"; case MONO_RGCTX_INFO_METHOD_RGCTX: return "METHOD_RGCTX"; case MONO_RGCTX_INFO_METHOD_CONTEXT: return "METHOD_CONTEXT"; @@ -2726,6 +2739,7 @@ info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type) case MONO_RGCTX_INFO_METHOD_GSHAREDVT_INFO: case MONO_RGCTX_INFO_GENERIC_METHOD_CODE: case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: + case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: case MONO_RGCTX_INFO_CLASS_FIELD: case MONO_RGCTX_INFO_FIELD_OFFSET: case MONO_RGCTX_INFO_METHOD_RGCTX: @@ -2784,9 +2798,17 @@ mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type) case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: case MONO_RGCTX_INFO_LOCAL_OFFSET: return MONO_PATCH_INFO_CLASS; + case MONO_RGCTX_INFO_CLASS_FIELD: case MONO_RGCTX_INFO_FIELD_OFFSET: return MONO_PATCH_INFO_FIELD; + case MONO_RGCTX_INFO_METHOD: + case MONO_RGCTX_INFO_METHOD_RGCTX: + case MONO_RGCTX_INFO_METHOD_FTNDESC: + case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER: + case MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT: + return MONO_PATCH_INFO_METHOD; default: + printf ("%d\n", info_type); g_assert_not_reached (); return (MonoJumpInfoType)-1; } diff --git a/src/mono/mono/mini/mini.h b/src/mono/mono/mini/mini.h index cd49dc45b9963f..767e2f1cb8ca58 100644 --- a/src/mono/mono/mini/mini.h +++ b/src/mono/mono/mini/mini.h @@ -1063,7 +1063,9 @@ typedef enum { /* Same as MONO_PATCH_INFO_METHOD_FTNDESC */ MONO_RGCTX_INFO_METHOD_FTNDESC = 33, /* mono_type_size () for a class */ - MONO_RGCTX_INFO_CLASS_SIZEOF = 34 + MONO_RGCTX_INFO_CLASS_SIZEOF = 34, + /* A gsharedvt_out wrapper for a method */ + MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER_VIRT = 35 } MonoRgctxInfoType; /* How an rgctx is passed to a method */