Skip to content

Commit

Permalink
[mono] Fix static virtual calls to generic methods from gshared code (#…
Browse files Browse the repository at this point in the history
…66739)

* [mono] Fix static virtual calls to generic methods from gshared code

When the resolved method from the vtable is generic, we need to inflate it.

For example, in the gshared method we are calling on `Interface.InterfaceMethod<int> ()` (the type argument can either be fixed or determined based on the gshared method's context), and this method is resolved to generic method `Class.InterfaceMethod<T> ()`. We then need to inflate the method and resolve `T` to `int`.

* Enable tests

* [mono][interp] Fix calls to static virtual methods via delegates

* [mono][aot] Fix constrained + ldftn

* [mono][gsharing] Fix constrained + ldftn from gshared method

We can'd determine at compile time the static method if the constrained_class is a generic parameteri. We defer the computation of the method at runtime, in a similar fashion with calls.

* Disable test suite on android

It hits an issue in the android test runner
  • Loading branch information
BrzVlad authored Apr 1, 2022
1 parent fc16965 commit 3efef3d
Show file tree
Hide file tree
Showing 5 changed files with 48 additions and 18 deletions.
2 changes: 1 addition & 1 deletion src/mono/mono/mini/interp/interp.c
Original file line number Diff line number Diff line change
Expand Up @@ -3778,7 +3778,7 @@ interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClauseArgs
del_imethod = mono_interp_get_imethod (mono_marshal_get_native_wrapper (del_imethod->method, FALSE, FALSE), error);
mono_error_assert_ok (error);
del->interp_invoke_impl = del_imethod;
} else if (del_imethod->method->flags & METHOD_ATTRIBUTE_VIRTUAL && !del->target && !m_class_is_valuetype (del_imethod->method->klass)) {
} else if ((m_method_is_virtual (del_imethod->method) && !m_method_is_static (del_imethod->method)) && !del->target && !m_class_is_valuetype (del_imethod->method->klass)) {
// 'this' is passed dynamically, we need to recompute the target method
// with each call
del_imethod = get_virtual_method (del_imethod, LOCAL_VAR (call_args_offset + MINT_STACK_SLOT_SIZE, MonoObject*)->vtable);
Expand Down
29 changes: 19 additions & 10 deletions src/mono/mono/mini/method-to-ir.c
Original file line number Diff line number Diff line change
Expand Up @@ -11326,23 +11326,27 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
case MONO_CEE_LDFTN: {
MonoInst *argconst;
MonoMethod *cil_method;
gboolean gshared_static_virtual = FALSE;

cmethod = mini_get_method (cfg, method, n, NULL, generic_context);
CHECK_CFG_ERROR;

if (constrained_class) {
if (m_method_is_static (cmethod) && mini_class_check_context_used (cfg, constrained_class))
// FIXME:
GENERIC_SHARING_FAILURE (CEE_LDFTN);
cmethod = get_constrained_method (cfg, image, n, cmethod, constrained_class, generic_context);
constrained_class = NULL;
CHECK_CFG_ERROR;
if (m_method_is_static (cmethod) && mini_class_check_context_used (cfg, constrained_class)) {
gshared_static_virtual = TRUE;
} else {
cmethod = get_constrained_method (cfg, image, n, cmethod, constrained_class, generic_context);
constrained_class = NULL;
CHECK_CFG_ERROR;
}
} else {
// we can't save token info if we have a constrained_class since
// n no longer represents the token for cmethod
mono_save_token_info (cfg, image, n, cmethod);
}

mono_class_init_internal (cmethod->klass);

mono_save_token_info (cfg, image, n, cmethod);

context_used = mini_method_check_context_used (cfg, cmethod);

cil_method = cmethod;
Expand All @@ -11356,7 +11360,7 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
/*
* Optimize the common case of ldftn+delegate creation
*/
if ((sp > stack_start) && (next_ip + 4 < end) && ip_in_bb (cfg, cfg->cbb, next_ip) && (next_ip [0] == CEE_NEWOBJ)) {
if (!gshared_static_virtual && (sp > stack_start) && (next_ip + 4 < end) && ip_in_bb (cfg, cfg->cbb, next_ip) && (next_ip [0] == CEE_NEWOBJ)) {
MonoMethod *ctor_method = mini_get_method (cfg, method, read32 (next_ip + 1), NULL, generic_context);
if (ctor_method && (m_class_get_parent (ctor_method->klass) == mono_defaults.multicastdelegate_class)) {
MonoInst *target_ins, *handle_ins;
Expand Down Expand Up @@ -11434,7 +11438,12 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
}
}

argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
if (gshared_static_virtual) {
argconst = emit_get_rgctx_virt_method (cfg, -1, constrained_class, cmethod, MONO_RGCTX_INFO_VIRT_METHOD);
constrained_class = NULL;
} else {
argconst = emit_get_rgctx_method (cfg, context_used, cmethod, MONO_RGCTX_INFO_METHOD);
}
ins = mono_emit_jit_icall (cfg, mono_ldftn, &argconst);
*sp++ = ins;

Expand Down
25 changes: 22 additions & 3 deletions src/mono/mono/mini/mini-generic-sharing.c
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,7 @@ inflate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoTempl
g_assert (is_ok (error));
return isig;
}
case MONO_RGCTX_INFO_VIRT_METHOD:
case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
Expand Down Expand Up @@ -2250,6 +2251,7 @@ instantiate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoT

return mini_get_interp_callbacks ()->create_method_pointer_llvmonly (m, FALSE, error);
}
case MONO_RGCTX_INFO_VIRT_METHOD:
case MONO_RGCTX_INFO_VIRT_METHOD_CODE: {
MonoJumpInfoVirtMethod *info = (MonoJumpInfoVirtMethod *)data;
MonoClass *iface_class = info->method->klass;
Expand All @@ -2270,10 +2272,24 @@ instantiate_info (MonoMemoryManager *mem_manager, MonoRuntimeGenericContextInfoT
g_assert (m_class_get_vtable (info->klass));
method = m_class_get_vtable (info->klass) [ioffset + slot];

method = mono_class_inflate_generic_method_checked (method, context, error);
return_val_if_nok (error, NULL);

if (mono_llvm_only) {
if (info->method->is_inflated) {
MonoGenericContext *method_ctx = mono_method_get_context (info->method);
if (method_ctx->method_inst != NULL) {
MonoGenericContext tmp_context;
tmp_context.class_inst = NULL;
tmp_context.method_inst = method_ctx->method_inst;

// The resolved virtual method can be generic, in which case we need to inflate
// it with the type parameters that we are calling with.
method = mono_class_inflate_generic_method_checked (method, &tmp_context, error);
return_val_if_nok (error, NULL);
}
}

if (oti->info_type == MONO_RGCTX_INFO_VIRT_METHOD) {
return method;
} else if (mono_llvm_only) {
gpointer arg = NULL;
addr = mini_llvmonly_load_method (method, FALSE, FALSE, &arg, error);

Expand Down Expand Up @@ -2668,6 +2684,7 @@ mono_rgctx_info_type_to_str (MonoRgctxInfoType type)
case MONO_RGCTX_INFO_BZERO: return "BZERO";
case MONO_RGCTX_INFO_NULLABLE_CLASS_BOX: return "NULLABLE_CLASS_BOX";
case MONO_RGCTX_INFO_NULLABLE_CLASS_UNBOX: return "NULLABLE_CLASS_UNBOX";
case MONO_RGCTX_INFO_VIRT_METHOD: return "VIRT_METHOD";
case MONO_RGCTX_INFO_VIRT_METHOD_CODE: return "VIRT_METHOD_CODE";
case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: return "VIRT_METHOD_BOX_TYPE";
case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO: return "DELEGATE_TRAMP_INFO";
Expand Down Expand Up @@ -2773,6 +2790,7 @@ info_equal (gpointer data1, gpointer data2, MonoRgctxInfoType info_type)
case MONO_RGCTX_INFO_SIG_GSHAREDVT_IN_TRAMPOLINE_CALLI:
case MONO_RGCTX_INFO_SIG_GSHAREDVT_OUT_TRAMPOLINE_CALLI:
return data1 == data2;
case MONO_RGCTX_INFO_VIRT_METHOD:
case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE: {
MonoJumpInfoVirtMethod *info1 = (MonoJumpInfoVirtMethod *)data1;
Expand Down Expand Up @@ -2835,6 +2853,7 @@ mini_rgctx_info_type_to_patch_info_type (MonoRgctxInfoType info_type)
return MONO_PATCH_INFO_METHOD;
case MONO_RGCTX_INFO_DELEGATE_TRAMP_INFO:
return MONO_PATCH_INFO_DELEGATE_TRAMPOLINE;
case MONO_RGCTX_INFO_VIRT_METHOD:
case MONO_RGCTX_INFO_VIRT_METHOD_CODE:
case MONO_RGCTX_INFO_VIRT_METHOD_BOX_TYPE:
return MONO_PATCH_INFO_VIRT_METHOD;
Expand Down
4 changes: 3 additions & 1 deletion src/mono/mono/mini/mini.h
Original file line number Diff line number Diff line change
Expand Up @@ -1076,7 +1076,9 @@ typedef enum {
/* The InterpMethod for a method */
MONO_RGCTX_INFO_INTERP_METHOD = 35,
/* The llvmonly interp entry for a method */
MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY = 36
MONO_RGCTX_INFO_LLVMONLY_INTERP_ENTRY = 36,
/* Same as VIRT_METHOD_CODE, but resolve MonoMethod* instead of code */
MONO_RGCTX_INFO_VIRT_METHOD = 37
} MonoRgctxInfoType;

/* How an rgctx is passed to a method */
Expand Down
6 changes: 3 additions & 3 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1526,9 +1526,6 @@
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/MethodImpl/CovariantReturns/UnitTest/CompatibleWithTest/**">
<Issue>Doesn't pass after LLVM AOT compilation.</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/StaticVirtualMethods/GenericContext/**">
<Issue>Static virtual methods are not yet implemented in the Mono runtime.</Issue>
</ExcludeList>
<ExcludeList Include="$(XunitTestBinBase)/Loader/classloader/StaticVirtualMethods/InterfaceVariance/**">
<Issue>Static virtual methods are not yet implemented in the Mono runtime.</Issue>
</ExcludeList>
Expand Down Expand Up @@ -3783,6 +3780,9 @@
<ExcludeList Include = "$(XunitTestBinBase)/tracing/eventpipe/reverseouter/**">
<Issue>Cannot run multiple apps on Android for subprocesses</Issue>
</ExcludeList>
<ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/StaticVirtualMethods/GenericContext/**">
<Issue>https://github.com/dotnet/runtime/issues/67359</Issue>
</ExcludeList>
</ItemGroup>

<ItemGroup Condition=" $(TargetOS) == 'Android' And '$(TargetArchitecture)' == 'arm64' " >
Expand Down

0 comments on commit 3efef3d

Please sign in to comment.