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

Change callvirt into calli for virtual delegates #83461

Merged
merged 27 commits into from
Apr 26, 2023
Merged
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a175351
Changed how NaN values are compared due to their multiple bit represe…
Nov 24, 2022
f9ed58c
Merge branch 'dotnet:main' into main
LeVladIonescu Nov 24, 2022
a381c06
Fixed typo
Nov 24, 2022
d91a78f
Merge branch 'dotnet:main' into main
LeVladIonescu Nov 25, 2022
1291fe3
Merge branch 'dotnet:main' into main
LeVladIonescu Dec 5, 2022
9a58bb3
Merge branch 'dotnet:main' into main
LeVladIonescu Dec 16, 2022
8db950e
Merge branch 'dotnet:main' into main
LeVladIonescu Jan 2, 2023
f587d24
Merge branch 'dotnet:main' into main
LeVladIonescu Jan 5, 2023
f5dbeaf
Merge branch 'dotnet:main' into main
LeVladIonescu Feb 1, 2023
287419e
Merge branch 'dotnet:main' into main
LeVladIonescu Feb 3, 2023
fbc1526
Work to make JIT virtual delegates not depend on the target_method
Mar 15, 2023
afd58dd
Merge branch 'dotnet:main' into delegates_issue
LeVladIonescu Mar 15, 2023
45f4845
Merge branch 'dotnet:main' into delegates_issue
LeVladIonescu Mar 15, 2023
91c3920
Switched order of how MonoObject is passed to the icall
Mar 16, 2023
81973db
Initialise pointer for build warning
Mar 16, 2023
3b0d6cb
Check return value in the test
Mar 16, 2023
f67f5fb
Retrieved method in icall and changed test location
Mar 17, 2023
dc2c525
Added check for unbox & rgctx trampolines and modified caching
Mar 31, 2023
4ba3366
Deleted unused vars
Mar 31, 2023
7b65607
Added test for lazy fetch trampoline (rgctx)
Apr 3, 2023
e7e8913
Merge branch 'dotnet:main' into delegates_issue
LeVladIonescu Apr 3, 2023
d6e1d3d
Added boxing for valuetype ref
Apr 12, 2023
0074bea
Added RequiresProcessIsolation property for tests in merged directory
Apr 13, 2023
be6daed
Using get_ftnptr callback and moving boxing inside the icall
Apr 19, 2023
596837e
Merge branch 'dotnet:main' into delegates_issue
LeVladIonescu Apr 20, 2023
414f451
Merge branch 'dotnet:main' into delegates_issue
LeVladIonescu Apr 24, 2023
1d2c4fb
Rremoved add_delegate_trampoline since it's not used anymore
Apr 24, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/mono/mono/metadata/jit-icall-reg.h
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ MONO_JIT_ICALL (mono_gc_wbarrier_generic_nostore_internal) \
MONO_JIT_ICALL (mono_gc_wbarrier_range_copy) \
MONO_JIT_ICALL (mono_gchandle_get_target_internal) \
MONO_JIT_ICALL (mono_generic_class_init) \
MONO_JIT_ICALL (mono_get_addr_compiled_method) \
MONO_JIT_ICALL (mono_get_assembly_object) \
MONO_JIT_ICALL (mono_get_method_object) \
MONO_JIT_ICALL (mono_get_native_calli_wrapper) \
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/metadata/loader-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,11 @@ typedef struct {
GHashTable *delegate_end_invoke_cache;
GHashTable *runtime_invoke_signature_cache;
GHashTable *runtime_invoke_sig_cache;
GHashTable *delegate_abstract_invoke_cache;

/*
* indexed by SignaturePointerPair
*/
GHashTable *delegate_abstract_invoke_cache;
GHashTable *delegate_bound_static_invoke_cache;

/*
Expand Down
21 changes: 7 additions & 14 deletions src/mono/mono/metadata/marshal-lightweight.c
Original file line number Diff line number Diff line change
Expand Up @@ -1990,7 +1990,7 @@ emit_delegate_end_invoke_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig)
}

static void
emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container)
emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container)
{
int local_i, local_len, local_delegates, local_d, local_target, local_res = 0;
int pos0, pos1, pos2;
Expand Down Expand Up @@ -2088,19 +2088,12 @@ emit_delegate_invoke_internal_ilgen (MonoMethodBuilder *mb, MonoMethodSignature

if (callvirt) {
if (!closed_over_null) {
/* if target_method is not really virtual, turn it into a direct call */
if (!(target_method->flags & METHOD_ATTRIBUTE_VIRTUAL) || m_class_is_valuetype (target_class)) {
mono_mb_emit_ldarg (mb, 1);
for (i = 1; i < sig->param_count; ++i)
mono_mb_emit_ldarg (mb, i + 1);
mono_mb_emit_op (mb, CEE_CALL, target_method);
} else {
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_op (mb, CEE_CASTCLASS, target_class);
for (i = 1; i < sig->param_count; ++i)
mono_mb_emit_ldarg (mb, i + 1);
mono_mb_emit_op (mb, CEE_CALLVIRT, target_method);
}
for (i = 1; i <= sig->param_count; ++i)
mono_mb_emit_ldarg (mb, i);
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_ldarg (mb, 0);
mono_mb_emit_icall (mb, mono_get_addr_compiled_method);
mono_mb_emit_op (mb, CEE_CALLI, target_method_sig);
} else {
mono_mb_emit_byte (mb, CEE_LDNULL);
for (i = 0; i < sig->param_count; ++i)
Expand Down
64 changes: 43 additions & 21 deletions src/mono/mono/metadata/marshal.c
Original file line number Diff line number Diff line change
Expand Up @@ -324,6 +324,7 @@ mono_marshal_init (void)
register_icall (monoeg_g_free, mono_icall_sig_void_ptr, FALSE);
register_icall (mono_object_isinst_icall, mono_icall_sig_object_object_ptr, TRUE);
register_icall (mono_struct_delete_old, mono_icall_sig_void_ptr_ptr, FALSE);
register_icall (mono_get_addr_compiled_method, mono_icall_sig_ptr_object_ptr, FALSE);
register_icall (mono_delegate_begin_invoke, mono_icall_sig_object_object_ptr, FALSE);
register_icall (mono_delegate_end_invoke, mono_icall_sig_object_object_ptr, FALSE);
register_icall (mono_gc_wbarrier_generic_nostore_internal, mono_icall_sig_void_ptr, TRUE);
Expand Down Expand Up @@ -2085,13 +2086,11 @@ free_signature_pointer_pair (SignaturePointerPair *pair)
MonoMethod *
mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt, gboolean static_method_with_first_arg_bound, MonoMethod *target_method)
{
MonoMethodSignature *sig, *invoke_sig;
MonoMethodSignature *sig, *invoke_sig, *target_method_sig = NULL;
MonoMethodBuilder *mb;
MonoMethod *res;
GHashTable *cache;
gpointer cache_key = NULL;
SignaturePointerPair key = { NULL, NULL };
SignaturePointerPair *new_key;
char *name;
MonoClass *target_class = NULL;
gboolean closed_over_null = FALSE;
Expand All @@ -2101,7 +2100,6 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
WrapperInfo *info;
WrapperSubtype subtype = WRAPPER_SUBTYPE_NONE;
MonoMemoryManager *mem_manager = NULL;
gboolean found;

g_assert (method && m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class &&
!strcmp (method->name, "Invoke"));
Expand Down Expand Up @@ -2129,6 +2127,11 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
}

closed_over_null = sig->param_count == mono_method_signature_internal (target_method)->param_count;

/*
* We don't want to use target_method's signature because it can be freed early
*/
target_method_sig = mono_method_signature_internal (target_method);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

The caching code needs to be modified to cache based on either the class or the signature. I.e. this code:

	} else if (callvirt) {
		GHashTable **cache_ptr;

		cache_ptr = &mono_method_get_wrapper_cache (method)->delegate_abstract_invoke_cache;

and:

	} else if (callvirt) {
		new_key = g_new0 (SignaturePointerPair, 1);
		*new_key = key;


if (static_method_with_first_arg_bound) {
Expand Down Expand Up @@ -2188,17 +2191,16 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt

cache_ptr = &mono_method_get_wrapper_cache (method)->delegate_abstract_invoke_cache;

/* We need to cache the signature+method pair */
/* We need to cache the signature */
mono_marshal_lock ();
if (!*cache_ptr)
*cache_ptr = g_hash_table_new_full (signature_pointer_pair_hash, (GEqualFunc)signature_pointer_pair_equal, (GDestroyNotify)free_signature_pointer_pair, NULL);
cache = *cache_ptr;
key.sig = invoke_sig;
key.pointer = target_method;
res = (MonoMethod *)g_hash_table_lookup (cache, &key);
cache = get_cache (cache_ptr,
(GHashFunc)mono_signature_hash,
(GCompareFunc)mono_metadata_signature_equal);
res = (MonoMethod *)g_hash_table_lookup (cache, invoke_sig);
mono_marshal_unlock ();
if (res)
return res;
cache_key = invoke_sig;
} else {
// Inflated methods should not be in this cache because it's not stored on the imageset.
g_assert (!method->is_inflated);
Expand Down Expand Up @@ -2239,7 +2241,7 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt
/* FIXME: Other subtypes */
mb->mem_manager = m_method_get_mem_manager (method);

get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, target_class, ctx, container);
get_marshal_cb ()->emit_delegate_invoke_internal (mb, sig, invoke_sig, target_method_sig, static_method_with_first_arg_bound, callvirt, closed_over_null, method, target_method, target_class, ctx, container);

get_marshal_cb ()->mb_skip_visibility (mb);

Expand All @@ -2251,13 +2253,6 @@ mono_marshal_get_delegate_invoke_internal (MonoMethod *method, gboolean callvirt

def = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL);
res = cache_generic_delegate_wrapper (cache, orig_method, def, ctx);
} else if (callvirt) {
new_key = g_new0 (SignaturePointerPair, 1);
*new_key = key;

res = mono_mb_create_and_cache_full (cache, new_key, mb, sig, sig->param_count + 16, info, &found);
if (found)
g_free (new_key);
} else {
res = mono_mb_create_and_cache_full (cache, cache_key, mb, sig, sig->param_count + 16, info, NULL);
}
Expand Down Expand Up @@ -5481,6 +5476,35 @@ mono_struct_delete_old (MonoClass *klass, char *ptr)
}
}

void*
mono_get_addr_compiled_method (MonoObject *object, MonoDelegate *del)
{
ERROR_DECL (error);
MonoMethod *res, *method = del->method;
gpointer addr;
gboolean need_unbox = FALSE;

if (object == NULL) {
mono_error_set_null_reference (error);
mono_error_set_pending_exception (error);
return NULL;
}

res = mono_object_get_virtual_method_internal (object, method);

addr = mono_compile_method_checked (res, error);

if (!is_ok (error)) {
mono_error_set_pending_exception (error);
return NULL;
}

need_unbox = m_class_is_valuetype (res->klass) && !m_class_is_valuetype (method->klass);
addr = mono_get_runtime_callbacks ()->add_delegate_trampolines (res, addr, need_unbox);

return addr;
}

void
ves_icall_System_Runtime_InteropServices_Marshal_DestroyStructure (gpointer src, MonoReflectionTypeHandle type, MonoError *error)
{
Expand Down Expand Up @@ -6255,8 +6279,6 @@ mono_marshal_free_dynamic_wrappers (MonoMethod *method)
*/
if (image->wrapper_caches.runtime_invoke_method_cache)
clear_runtime_invoke_method_cache (image->wrapper_caches.runtime_invoke_method_cache, method);
if (image->wrapper_caches.delegate_abstract_invoke_cache)
g_hash_table_foreach_remove (image->wrapper_caches.delegate_abstract_invoke_cache, signature_pointer_pair_matches_pointer, method);
// FIXME: Need to clear the caches in other images as well
if (image->wrapper_caches.delegate_bound_static_invoke_cache)
g_hash_table_remove (image->wrapper_caches.delegate_bound_static_invoke_cache, mono_method_signature_internal (method));
Expand Down
6 changes: 5 additions & 1 deletion src/mono/mono/metadata/marshal.h
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ typedef struct {
void (*emit_runtime_invoke_dynamic) (MonoMethodBuilder *mb);
void (*emit_delegate_begin_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig);
void (*emit_delegate_end_invoke) (MonoMethodBuilder *mb, MonoMethodSignature *sig);
void (*emit_delegate_invoke_internal) (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container);
void (*emit_delegate_invoke_internal) (MonoMethodBuilder *mb, MonoMethodSignature *sig, MonoMethodSignature *invoke_sig, MonoMethodSignature *target_method_sig, gboolean static_method_with_first_arg_bound, gboolean callvirt, gboolean closed_over_null, MonoMethod *method, MonoMethod *target_method, MonoClass *target_class, MonoGenericContext *ctx, MonoGenericContainer *container);
void (*emit_synchronized_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoGenericContext *ctx, MonoGenericContainer *container, MonoMethod *enter_method, MonoMethod *exit_method, MonoMethod *gettypefromhandle_method);
void (*emit_unbox_wrapper) (MonoMethodBuilder *mb, MonoMethod *method);
void (*emit_array_accessor_wrapper) (MonoMethodBuilder *mb, MonoMethod *method, MonoMethodSignature *sig, MonoGenericContext *ctx);
Expand Down Expand Up @@ -624,6 +624,10 @@ ICALL_EXPORT
void
mono_struct_delete_old (MonoClass *klass, char *ptr);

ICALL_EXPORT
void*
mono_get_addr_compiled_method (MonoObject *object, MonoDelegate *del);

int
mono_emit_marshal (EmitMarshalContext *m, int argnum, MonoType *t,
MonoMarshalSpec *spec, int conv_arg,
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/object-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -699,6 +699,7 @@ typedef struct {
void (*interp_jit_info_foreach)(InterpJitInfoFunc func, gpointer user_data);
gboolean (*interp_sufficient_stack)(gsize size);
void (*init_class) (MonoClass *klass);
gpointer (*add_delegate_trampolines) (MonoMethod *method, gpointer compiled_method, gboolean need_unbox);
MonoArray *(*get_trace) (MonoException *exc, gint32 skip, MonoBoolean need_file_info);
MonoBoolean (*get_frame_info) (gint32 skip, MonoMethod **out_method,
MonoDebugSourceLocation **out_location,
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -4561,6 +4561,7 @@ mini_init (const char *filename)
callbacks.init_class = init_class;
callbacks.get_trace = mono_get_trace;
callbacks.get_frame_info = mono_get_frame_info;
callbacks.add_delegate_trampolines = mono_add_delegate_trampolines;

mono_install_callbacks (&callbacks);

Expand Down
5 changes: 5 additions & 0 deletions src/mono/mono/mini/mini-trampolines.c
Original file line number Diff line number Diff line change
Expand Up @@ -1468,6 +1468,11 @@ mono_create_delegate_trampoline (MonoClass *klass)
return mono_create_delegate_trampoline_info (klass, NULL, FALSE)->invoke_impl;
}

gpointer mono_add_delegate_trampolines (MonoMethod *method, gpointer compiled_method, gboolean need_unbox)
{
return mini_add_method_trampoline (method, compiled_method, mono_method_needs_static_rgctx_invoke (method, TRUE), need_unbox);
}

gpointer
mono_create_rgctx_lazy_fetch_trampoline (guint32 offset)
{
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/mini.h
Original file line number Diff line number Diff line change
Expand Up @@ -2269,6 +2269,7 @@ gpointer mono_create_jump_trampoline (MonoMethod *method,
gpointer mono_create_jit_trampoline (MonoMethod *method, MonoError *error);
gpointer mono_create_jit_trampoline_from_token (MonoImage *image, guint32 token);
gpointer mono_create_delegate_trampoline (MonoClass *klass);
gpointer mono_add_delegate_trampolines (MonoMethod *method, gpointer compiled_method, gboolean need_unbox);
MonoDelegateTrampInfo* mono_create_delegate_trampoline_info (MonoClass *klass, MonoMethod *method, gboolean is_virtual);
gpointer mono_create_rgctx_lazy_fetch_trampoline (guint32 offset);
gpointer mono_create_static_rgctx_trampoline (MonoMethod *m, gpointer addr);
Expand Down
53 changes: 53 additions & 0 deletions src/tests/JIT/Methodical/delegate/GSDelegate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Reflection;


public interface IGetContents<T> {
(string, int, T) GetContents();
}
public struct MyStruct<T> : IGetContents<T> {
public string s;
public int a;
public T t;

public (string, int, T) GetContents()
{
return (s, a, t);
}
}

public class Program {

public delegate (string, int, T) MyDelegate<T>(IGetContents<T> arg);

public static int Main(string[] args)
{
int retVal = 100;

try {
MyStruct<string> myStruct = new MyStruct<string>();
myStruct.s = "test1";
myStruct.a = 42;
myStruct.t = "test2";

MethodInfo mi = typeof(IGetContents<string>).GetMethod("GetContents");
MyDelegate<string> func = (MyDelegate<string>)mi.CreateDelegate(typeof(MyDelegate<string>));

(string c1, int c2, string c3) = func(myStruct);
if (c1 != "test1")
retVal = 1;
if (c2 != 42)
retVal = 2;
if (c3 != "test2")
retVal = 3;
} catch (Exception e) {
Console.WriteLine(e);
retVal = 1;
}

return retVal;
}
}
12 changes: 12 additions & 0 deletions src/tests/JIT/Methodical/delegate/GSDelegate.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>PdbOnly</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="GSDelegate.cs" />
</ItemGroup>
</Project>
24 changes: 24 additions & 0 deletions src/tests/JIT/Methodical/delegate/VirtualDelegate.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.


using System;
using System.Runtime.InteropServices;

public class VirtualDelegate
{
public static int Main () {
int retVal = 100;
try {
var del = (Func<string, string>)Delegate.CreateDelegate (typeof (Func<string, string>), null, typeof (object).GetMethod ("ToString"));
if (del ("FOO") != "FOO")
retVal = 1;
} catch(Exception e) {
Console.WriteLine(e);
retVal = 1;
}

return retVal;

}
}
12 changes: 12 additions & 0 deletions src/tests/JIT/Methodical/delegate/VirtualDelegate.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<PropertyGroup>
<DebugType>PdbOnly</DebugType>
<Optimize>True</Optimize>
</PropertyGroup>
<ItemGroup>
<Compile Include="VirtualDelegate.cs" />
</ItemGroup>
</Project>