Skip to content

Commit 77a8ffa

Browse files
committed
[mono] Fix support for calls to static virtual methods from gshared code.
Fixes #65002. When called from gshared code, these methods cannot be resolved at compile time, since they depend on the constrained class which is only known at runtime. * For calls from normal gshared code, load the method address from the rgctx. * For calls from gsharedvt code, extend the existing mono_gsharedvt_constrained_call () JIT icall to be able to handle static virtual methods.
1 parent d85ec4e commit 77a8ffa

File tree

2 files changed

+65
-38
lines changed

2 files changed

+65
-38
lines changed

src/mono/mono/mini/jit-icalls.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1421,6 +1421,7 @@ constrained_gsharedvt_call_setup (gpointer mp, MonoMethod *cmethod, MonoClass *k
14211421
*
14221422
* Make a call to CMETHOD using the receiver MP, which is assumed to be of type KLASS. ARGS contains
14231423
* the arguments to the method in the format used by mono_runtime_invoke_checked ().
1424+
* MP is NULL if CMETHOD is a static virtual method.
14241425
*/
14251426
MonoObject*
14261427
mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *klass, guint8 *deref_args, gpointer *args)

src/mono/mono/mini/method-to-ir.c

Lines changed: 64 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -3739,12 +3739,15 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe
37393739
* plus some simple interface calls enough to support AsyncTaskMethodBuilder.
37403740
*/
37413741

3742-
args [0] = sp [0];
3742+
if (fsig->hasthis)
3743+
args [0] = sp [0];
3744+
else
3745+
EMIT_NEW_PCONST (cfg, args [0], NULL);
37433746
args [1] = emit_get_rgctx_method (cfg, mono_method_check_context_used (cmethod), cmethod, MONO_RGCTX_INFO_METHOD);
37443747
args [2] = mini_emit_get_rgctx_klass (cfg, mono_class_check_context_used (constrained_class), constrained_class, MONO_RGCTX_INFO_KLASS);
37453748

3746-
/* !fsig->hasthis is for the wrapper for the Object.GetType () icall */
3747-
if (fsig->hasthis && fsig->param_count) {
3749+
/* !fsig->hasthis is for the wrapper for the Object.GetType () icall or static virtual methods */
3750+
if ((fsig->hasthis || m_method_is_static (cmethod)) && fsig->param_count) {
37483751
/* Call mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *klass, gboolean *deref_args, gpointer *args) */
37493752
gboolean has_gsharedvt = FALSE;
37503753
for (int i = 0; i < fsig->param_count; ++i) {
@@ -3783,12 +3786,14 @@ handle_constrained_gsharedvt_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMe
37833786
MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STOREI1_MEMBASE_IMM, args [3]->dreg, i, 0);
37843787
}
37853788

3789+
MonoInst *arg = sp [i + fsig->hasthis];
3790+
37863791
if (mini_is_gsharedvt_type (fsig->params [i]) || MONO_TYPE_IS_PRIMITIVE (fsig->params [i]) || MONO_TYPE_ISSTRUCT (fsig->params [i])) {
3787-
EMIT_NEW_VARLOADA_VREG (cfg, ins, sp [i + 1]->dreg, fsig->params [i]);
3792+
EMIT_NEW_VARLOADA_VREG (cfg, ins, arg->dreg, fsig->params [i]);
37883793
addr_reg = ins->dreg;
37893794
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, i * sizeof (target_mgreg_t), addr_reg);
37903795
} else {
3791-
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, i * sizeof (target_mgreg_t), sp [i + 1]->dreg);
3796+
EMIT_NEW_STORE_MEMBASE (cfg, ins, OP_STORE_MEMBASE_REG, args [4]->dreg, i * sizeof (target_mgreg_t), arg->dreg);
37923797
}
37933798
}
37943799
} else {
@@ -5726,9 +5731,9 @@ handle_constrained_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignat
57265731
}
57275732

57285733
if (m_method_is_static (cmethod)) {
5729-
/* Call to an abstract static method */
5734+
/* Call to an abstract static method, handled normally */
57305735
return NULL;
5731-
} if (constrained_partial_call) {
5736+
} else if (constrained_partial_call) {
57325737
gboolean need_box = TRUE;
57335738

57345739
/*
@@ -7325,6 +7330,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
73257330
gboolean direct_icall; direct_icall = FALSE;
73267331
gboolean tailcall_calli; tailcall_calli = FALSE;
73277332
gboolean noreturn; noreturn = FALSE;
7333+
gboolean gshared_static_virtual; gshared_static_virtual = FALSE;
73287334
#ifdef TARGET_WASM
73297335
gboolean needs_stack_walk; needs_stack_walk = FALSE;
73307336
#endif
@@ -7357,20 +7363,24 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
73577363

73587364
MonoMethod *cil_method; cil_method = cmethod;
73597365
if (constrained_class) {
7360-
if (m_method_is_static (cil_method) && mini_class_check_context_used (cfg, constrained_class))
7361-
// FIXME:
7362-
GENERIC_SHARING_FAILURE (CEE_CALL);
7363-
7364-
cmethod = get_constrained_method (cfg, image, token, cil_method, constrained_class, generic_context);
7365-
CHECK_CFG_ERROR;
7366+
if (m_method_is_static (cil_method) && mini_class_check_context_used (cfg, constrained_class)) {
7367+
/* get_constrained_method () doesn't work on the gparams used by generic sharing */
7368+
// FIXME: Other configurations
7369+
//if (!cfg->gsharedvt)
7370+
// GENERIC_SHARING_FAILURE (CEE_CALL);
7371+
gshared_static_virtual = TRUE;
7372+
} else {
7373+
cmethod = get_constrained_method (cfg, image, token, cil_method, constrained_class, generic_context);
7374+
CHECK_CFG_ERROR;
73667375

7367-
if (m_class_is_enumtype (constrained_class) && !strcmp (cmethod->name, "GetHashCode")) {
7368-
/* Use the corresponding method from the base type to avoid boxing */
7369-
MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
7370-
g_assert (base_type);
7371-
constrained_class = mono_class_from_mono_type_internal (base_type);
7372-
cmethod = get_method_nofail (constrained_class, cmethod->name, 0, 0);
7373-
g_assert (cmethod);
7376+
if (m_class_is_enumtype (constrained_class) && !strcmp (cmethod->name, "GetHashCode")) {
7377+
/* Use the corresponding method from the base type to avoid boxing */
7378+
MonoType *base_type = mono_class_enum_basetype_internal (constrained_class);
7379+
g_assert (base_type);
7380+
constrained_class = mono_class_from_mono_type_internal (base_type);
7381+
cmethod = get_method_nofail (constrained_class, cmethod->name, 0, 0);
7382+
g_assert (cmethod);
7383+
}
73747384
}
73757385
}
73767386

@@ -7400,7 +7410,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
74007410
#endif
74017411
}
74027412

7403-
if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT)) {
7413+
if (!virtual_ && (cmethod->flags & METHOD_ATTRIBUTE_ABSTRACT) && !gshared_static_virtual) {
74047414
if (!mono_class_is_interface (method->klass))
74057415
emit_bad_image_failure (cfg, method, cil_method);
74067416
else
@@ -7510,7 +7520,8 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
75107520
if (constrained_class) {
75117521
ins = handle_constrained_call (cfg, cmethod, fsig, constrained_class, sp, &cdata, &cmethod, &virtual_, &emit_widen);
75127522
CHECK_CFG_EXCEPTION;
7513-
constrained_class = NULL;
7523+
if (!gshared_static_virtual)
7524+
constrained_class = NULL;
75147525
if (ins)
75157526
goto call_end;
75167527
}
@@ -7935,41 +7946,56 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
79357946
/* Generic sharing */
79367947

79377948
/*
7949+
* Calls to generic methods from shared code cannot go through the trampoline infrastructure
7950+
* in some cases, because the called method might end up being different on every call.
7951+
* Load the called method address from the rgctx and do an indirect call in these cases.
79387952
* Use this if the callee is gsharedvt sharable too, since
79397953
* at runtime we might find an instantiation so the call cannot
79407954
* be patched (the 'no_patch' code path in mini-trampolines.c).
79417955
*/
7942-
if (context_used && !imt_arg && !array_rank && !delegate_invoke &&
7943-
(!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7944-
!mono_class_generic_sharing_enabled (cmethod->klass)) &&
7945-
(!virtual_ || MONO_METHOD_IS_FINAL (cmethod) ||
7946-
!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL))) {
7956+
gboolean gshared_indirect;
7957+
gshared_indirect = context_used && !imt_arg && !array_rank && !delegate_invoke;
7958+
if (gshared_indirect)
7959+
gshared_indirect = (!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
7960+
!mono_class_generic_sharing_enabled (cmethod->klass) ||
7961+
gshared_static_virtual);
7962+
if (gshared_indirect)
7963+
gshared_indirect = (!virtual_ || MONO_METHOD_IS_FINAL (cmethod) ||
7964+
!(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL));
7965+
if (gshared_indirect) {
79477966
INLINE_FAILURE ("gshared");
79487967

79497968
g_assert (cfg->gshared && cmethod);
79507969
g_assert (!addr);
79517970

7952-
/*
7953-
* We are compiling a call to a
7954-
* generic method from shared code,
7955-
* which means that we have to look up
7956-
* the method in the rgctx and do an
7957-
* indirect call.
7958-
*/
79597971
if (fsig->hasthis)
79607972
MONO_EMIT_NEW_CHECK_THIS (cfg, sp [0]->dreg);
79617973

79627974
if (cfg->llvm_only) {
7963-
if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig))
7975+
if (cfg->gsharedvt && mini_is_gsharedvt_variable_signature (fsig)) {
7976+
/* Handled in handle_constrained_gsharedvt_call () */
7977+
g_assert (!gshared_static_virtual);
79647978
addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GSHAREDVT_OUT_WRAPPER);
7965-
else
7966-
addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_FTNDESC);
7979+
} else {
7980+
if (gshared_static_virtual)
7981+
addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
7982+
else
7983+
addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD_FTNDESC);
7984+
}
79677985
// FIXME: Avoid initializing imt_arg/vtable_arg
79687986
ins = mini_emit_llvmonly_calli (cfg, fsig, sp, addr);
79697987
if (inst_tailcall) // FIXME
79707988
mono_tailcall_print ("missed tailcall context_used_llvmonly %s -> %s\n", method->name, cmethod->name);
79717989
} else {
7972-
addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7990+
if (gshared_static_virtual) {
7991+
/*
7992+
* cmethod is a static interface method, the actual called method at runtime
7993+
* needs to be computed using constrained_class and cmethod.
7994+
*/
7995+
addr = emit_get_rgctx_virt_method (cfg, mono_class_check_context_used (constrained_class), constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD_CODE);
7996+
} else {
7997+
addr = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
7998+
}
79737999
if (inst_tailcall)
79748000
mono_tailcall_print ("%s tailcall_calli#2 %s -> %s\n", tailcall_calli ? "making" : "missed", method->name, cmethod->name);
79758001
tailcall = tailcall_calli;

0 commit comments

Comments
 (0)