diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c
index ed38874fbf5f3c..60eecf962a774c 100644
--- a/src/mono/mono/mini/interp/interp.c
+++ b/src/mono/mono/mini/interp/interp.c
@@ -3975,7 +3975,12 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
} 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);
+ MonoObject *obj = LOCAL_VAR (call_args_offset + MINT_STACK_SLOT_SIZE, MonoObject*);
+ del_imethod = get_virtual_method (del_imethod, obj->vtable);
+ if (m_class_is_valuetype (del_imethod->method->klass)) {
+ // We are calling into a value type method, `this` needs to be unboxed
+ LOCAL_VAR (call_args_offset + MINT_STACK_SLOT_SIZE, gpointer) = mono_object_unbox_internal (obj);
+ }
} else {
del->interp_invoke_impl = del_imethod;
}
@@ -3998,7 +4003,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
MonoObject *this_arg = del->target;
// replace the MonoDelegate* on the stack with 'this' pointer
- if (m_class_is_valuetype (this_arg->vtable->klass) && m_class_is_valuetype (cmethod->method->klass)) {
+ if (m_class_is_valuetype (cmethod->method->klass)) {
gpointer unboxed = mono_object_unbox_internal (this_arg);
LOCAL_VAR (call_args_offset, gpointer) = unboxed;
} else {
@@ -4098,7 +4103,7 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
ip += 5;
// FIXME push/pop LMF
cmethod = get_virtual_method_fast (cmethod, this_arg->vtable, slot);
- if (m_class_is_valuetype (this_arg->vtable->klass) && m_class_is_valuetype (cmethod->method->klass)) {
+ if (m_class_is_valuetype (cmethod->method->klass)) {
/* unbox */
gpointer unboxed = mono_object_unbox_internal (this_arg);
LOCAL_VAR (call_args_offset, gpointer) = unboxed;
@@ -4148,29 +4153,6 @@ mono_interp_exec_method (InterpFrame *frame, ThreadContext *context, FrameClause
goto call;
}
- MINT_IN_CASE(MINT_CALLVIRT) {
- // FIXME CALLVIRT opcodes are not used on netcore. We should kill them.
- cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]];
- return_offset = ip [1];
- call_args_offset = ip [2];
-
- MonoObject *this_arg = LOCAL_VAR (call_args_offset, MonoObject*);
-
- // FIXME push/pop LMF
- cmethod = get_virtual_method (cmethod, this_arg->vtable);
- if (m_class_is_valuetype (this_arg->vtable->klass) && m_class_is_valuetype (cmethod->method->klass)) {
- /* unbox */
- gpointer unboxed = mono_object_unbox_internal (this_arg);
- LOCAL_VAR (call_args_offset, gpointer) = unboxed;
- }
-
-#ifdef ENABLE_EXPERIMENT_TIERED
- ip += 5;
-#else
- ip += 4;
-#endif
- goto call;
- }
MINT_IN_CASE(MINT_CALL) {
cmethod = (InterpMethod*)frame->imethod->data_items [ip [3]];
return_offset = ip [1];
diff --git a/src/mono/mono/mini/interp/mintops.def b/src/mono/mono/mini/interp/mintops.def
index f8b65c48c404d2..aa5f18396c1b39 100644
--- a/src/mono/mono/mini/interp/mintops.def
+++ b/src/mono/mono/mini/interp/mintops.def
@@ -679,7 +679,6 @@ OPDEF(MINT_ARRAY_IS_PRIMITIVE, "array_is_primitive", 3, 1, 1, MintOpNoArgs)
/* Calls */
OPDEF(MINT_CALL, "call", 4, 1, 1, MintOpMethodToken)
-OPDEF(MINT_CALLVIRT, "callvirt", 4, 1, 1, MintOpMethodToken)
OPDEF(MINT_CALLVIRT_FAST, "callvirt.fast", 5, 1, 1, MintOpMethodToken)
OPDEF(MINT_CALL_DELEGATE, "call.delegate", 5, 1, 1, MintOpTwoShorts)
OPDEF(MINT_CALLI, "calli", 4, 1, 2, MintOpNoArgs)
diff --git a/src/mono/mono/mini/interp/transform.c b/src/mono/mono/mini/interp/transform.c
index d5c7068882f120..bc1866d9421b66 100644
--- a/src/mono/mono/mini/interp/transform.c
+++ b/src/mono/mono/mini/interp/transform.c
@@ -3516,8 +3516,6 @@ interp_transform_call (TransformData *td, MonoMethod *method, MonoMethod *target
} else if (is_virtual) {
interp_add_ins (td, MINT_CALLVIRT_FAST);
td->last_ins->data [1] = get_virt_method_slot (target_method);
- } else if (is_virtual) {
- interp_add_ins (td, MINT_CALLVIRT);
} else {
interp_add_ins (td, MINT_CALL);
}
diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_79354/Runtime_79354.cs b/src/tests/JIT/Regression/JitBlue/Runtime_79354/Runtime_79354.cs
new file mode 100644
index 00000000000000..8114d964526fc0
--- /dev/null
+++ b/src/tests/JIT/Regression/JitBlue/Runtime_79354/Runtime_79354.cs
@@ -0,0 +1,45 @@
+// 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 {
+ (string, int, string) GetContents();
+}
+
+public struct MyStruct : IGetContents {
+ public string s1;
+ public int a;
+ public string s2;
+
+ public (string, int, string) GetContents()
+ {
+ return (s1, a, s2);
+ }
+}
+
+public class Program {
+
+ public delegate (string, int, string) MyDelegate(IGetContents arg);
+
+ public static int Main(string[] args)
+ {
+ MyStruct str = new MyStruct();
+ str.s1 = "test1";
+ str.a = 42;
+ str.s2 = "test2";
+
+ MethodInfo mi = typeof(IGetContents).GetMethod("GetContents");
+ MyDelegate func = (MyDelegate)mi.CreateDelegate(typeof(MyDelegate));
+
+ (string c1, int c2, string c3) = func(str);
+ if (c1 != "test1")
+ return 1;
+ if (c2 != 42)
+ return 2;
+ if (c3 != "test2")
+ return 3;
+ return 100;
+ }
+}
diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_79354/Runtime_79354.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_79354/Runtime_79354.csproj
new file mode 100644
index 00000000000000..75e7d24ec6fd6d
--- /dev/null
+++ b/src/tests/JIT/Regression/JitBlue/Runtime_79354/Runtime_79354.csproj
@@ -0,0 +1,9 @@
+
+
+ Exe
+ True
+
+
+
+
+
diff --git a/src/tests/issues.targets b/src/tests/issues.targets
index 07db2c1b26b0b8..2205c2e680fa16 100644
--- a/src/tests/issues.targets
+++ b/src/tests/issues.targets
@@ -2913,6 +2913,9 @@
https://github.com/dotnet/runtime/issues/57350
+
+ https://github.com/dotnet/runtime/issues/57350
+
https://github.com/dotnet/runtime/issues/57350