Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono] Extend mono_gsharedvt_constrained_call JIT icall to handle static virtual methods #90875

Merged
merged 5 commits into from
Aug 24, 2023

Conversation

kotlarmilos
Copy link
Member

@kotlarmilos kotlarmilos commented Aug 21, 2023

This PR extends mono_gsharedvt_constrained_call JIT icall to handle static virtual methods. If cmethod is a static virtual method, this arg should be null.

Contributes to #90732

@kotlarmilos kotlarmilos added this to the 8.0.0 milestone Aug 21, 2023
@kotlarmilos kotlarmilos requested a review from BrzVlad August 21, 2023 16:40
@kotlarmilos kotlarmilos self-assigned this Aug 21, 2023
@ghost
Copy link

ghost commented Aug 21, 2023

Tagging subscribers to 'os-ios': @steveisok, @akoeplinger, @kotlarmilos
See info in area-owners.md if you want to be subscribed.

Issue Details

This PR extends mono_gsharedvt_constrained_call JIT icall to handle static virtual methods. If cmethod is a static virtual method, the receiver's type is null. In these scenarios, the call cannot be made through constrained_gsharedvt_call_setup, and the type is retrieved from vtable of a class.

Hopefully fixes #90732

Author: kotlarmilos
Assignees: kotlarmilos
Labels:

area-Codegen-AOT-mono, os-ios

Milestone: 8.0.0

@LeVladIonescu
Copy link
Contributor

Can you also add the testcase here?

@kotlarmilos
Copy link
Member Author

Good idea. We will add a test case once we confirm this is the right fix.

@@ -1449,8 +1449,8 @@ mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *kl
m = info->method;
break;
default:
/* Object.GetType () is an intrinsic under netcore */
if (!mono_class_is_ginst (cmethod->klass) && !cmethod->is_inflated && !strcmp (cmethod->name, "GetType")) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So this code was a special case for call to Object.GetType () by returning vt->type. Your change just makes every static virtual call do the same thing instead of running the actual method !?

Copy link
Member Author

@kotlarmilos kotlarmilos Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, here is the reasoning behind this change. Before the optimization of constrained calls from gshared methods in #79339, the type was retrieved as vt = mono_class_vtable_checked (klass, error);, in the same way as it is done here. After that, in #65126 arguments of static virtual methods are handled in the same way as for Object.GetType ():

/* !fsig->hasthis is for the wrapper for the Object.GetType () icall or static virtual methods */
if ((fsig->hasthis || m_method_is_static (cmethod)) && fsig->param_count) {

Additionally, there is a note that the mp is null in case of static virtual methods, which is why the constrained_gsharedvt_call_setup fails:

/*
* mono_gsharedvt_constrained_call:
*
* Make a call to CMETHOD using the receiver MP, which is assumed to be of type KLASS. ARGS contains
* the arguments to the method in the format used by mono_runtime_invoke_checked ().
* MP is NULL if CMETHOD is a static virtual method.
*/
MonoObject*
mono_gsharedvt_constrained_call (gpointer mp, MonoMethod *cmethod, MonoClass *klass,
MonoGsharedvtConstrainedCallInfo *info, guint8 *deref_args, gpointer *args)
{

My assumption was that it shouldn't reach the constrained_gsharedvt_call_setup in this case.

Copy link
Member Author

@kotlarmilos kotlarmilos Aug 22, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After offline discussion, the above ^ was a wild assumption :) The static methods should be handled in constrained_gsharedvt_call_setup as @BrzVlad suggested.

@BrzVlad
Copy link
Member

BrzVlad commented Aug 22, 2023

As far as my understanding goes, when you get below to constrained_gsharedvt_call_setup, you have the constrained class in klass (which should implement the method to be called) and the static virtual method to be called in cmethod (double check this). This is enough information to resolve the actual implementation of cmethod in klass, just need find out why the code crashes.

@kotlarmilos
Copy link
Member Author

kotlarmilos commented Aug 22, 2023

As far as my understanding goes, when you get below to constrained_gsharedvt_call_setup, you have the constrained class in klass (which should implement the method to be called) and the static virtual method to be called in cmethod (double check this). This is enough information to resolve the actual implementation of cmethod in klass, just need find out why the code crashes.

Thanks for feedback! It should be already handled here:

/* Lookup the virtual method */
mono_class_setup_vtable (klass);
g_assert (m_class_get_vtable (klass));
vt_slot = mono_method_get_vtable_slot (cmethod);
if (mono_class_is_interface (cmethod->klass)) {
iface_offset = mono_class_interface_offset (klass, cmethod->klass);
g_assert (iface_offset != -1);
vt_slot += iface_offset;
}
m = m_class_get_vtable (klass) [vt_slot];
if (cmethod->is_inflated) {
m = mono_class_inflate_generic_method_full_checked (m, NULL, mono_method_get_context (cmethod), error);
return_val_if_nok (error, NULL);
}

Do you think we can avoid this_obj assignment if receiver's type (mp) is null?

@kotlarmilos kotlarmilos marked this pull request as ready for review August 22, 2023 16:04
@kotlarmilos kotlarmilos changed the title [mono][wip] Extend mono_gsharedvt_constrained_call JIT icall to handle static virtual methods [mono] Extend mono_gsharedvt_constrained_call JIT icall to handle static virtual methods Aug 22, 2023
@kotlarmilos
Copy link
Member Author

/azp run runtime-extra-platforms

@azure-pipelines
Copy link

Azure Pipelines successfully started running 1 pipeline(s).

public class test
{
[Fact]
public static int TestEntryPoint()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test seems quite trivial. Would it even fail on CI ? Or is the failure dependent on compilation flags that we don't use on CI.

Copy link
Member Author

@kotlarmilos kotlarmilos Aug 23, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue occurred in full AOT mode only. This test is used locally to reproduce and fix the issue, which is similar to the customer reported issue. Do you think it should be expanded?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you disable your fix will this test fail on CI currently ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before adding a new test, we should verify if it is not already covered on the CI. This will be done once the AOT job is fixed. The customer issue won't be closed until we verify that this case has test coverage.

@kotlarmilos kotlarmilos merged commit 0c4329b into dotnet:main Aug 24, 2023
@kotlarmilos
Copy link
Member Author

/backport to release/8.0

@github-actions
Copy link
Contributor

Started backporting to release/8.0: https://github.com/dotnet/runtime/actions/runs/5964549560

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants