Skip to content

Commit b6cc822

Browse files
authored
[mini] Dynamically allocate a buffer for large runtime invoke results (#58215)
* [mini] Dynamically allocate a buffer for large runtime invoke results If the return type is a struct that's bigger than our buffer, malloc a buffer for it instead of using a fixed-size stack buffer Also take the ref-return logic from #52501 and add it to the non-LLVM runtime-invoke This makes the `System.Runtime` testsuite (particularly `InvokeRefReturnNetcoreTests`) pass on M1 MacCatalyst FullAOT. Related to #58190 * Throw nullbyrefreturn exception for non-LLVM dyn invoke Fixes various tests in InvokeRefReturnNetcoreTests
1 parent c584001 commit b6cc822

File tree

1 file changed

+67
-8
lines changed

1 file changed

+67
-8
lines changed

src/mono/mono/mini/mini-runtime.c

Lines changed: 67 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2998,6 +2998,8 @@ typedef struct {
29982998
gpointer *wrapper_arg;
29992999
} RuntimeInvokeInfo;
30003000

3001+
#define MONO_SIZEOF_DYN_CALL_RET_BUF 256
3002+
30013003
static RuntimeInvokeInfo*
30023004
create_runtime_invoke_info (MonoMethod *method, gpointer compiled_method, gboolean callee_gsharedvt, gboolean use_interp, MonoError *error)
30033005
{
@@ -3156,8 +3158,9 @@ mono_llvmonly_runtime_invoke (MonoMethod *method, RuntimeInvokeInfo *info, void
31563158
{
31573159
MonoMethodSignature *sig = info->sig;
31583160
MonoObject *(*runtime_invoke) (MonoObject *this_obj, void **params, MonoObject **exc, void* compiled_method);
3161+
gboolean retval_malloc = FALSE;
31593162
gpointer retval_ptr;
3160-
guint8 retval [256];
3163+
guint8 retval [MONO_SIZEOF_DYN_CALL_RET_BUF];
31613164
int i, pindex;
31623165

31633166
error_init (error);
@@ -3184,7 +3187,21 @@ mono_llvmonly_runtime_invoke (MonoMethod *method, RuntimeInvokeInfo *info, void
31843187
if (sig->hasthis)
31853188
args [pindex ++] = &obj;
31863189
if (sig->ret->type != MONO_TYPE_VOID) {
3187-
retval_ptr = &retval;
3190+
if (info->ret_box_class && !sig->ret->byref &&
3191+
(sig->ret->type == MONO_TYPE_VALUETYPE ||
3192+
(sig->ret->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (sig->ret)))) {
3193+
// if the return type is a struct and its too big for the stack buffer, malloc instead
3194+
MonoClass *ret_klass = mono_class_from_mono_type_internal (sig->ret);
3195+
g_assert (!mono_class_has_failure (ret_klass));
3196+
int32_t inst_size = mono_class_instance_size (ret_klass);
3197+
if (inst_size > MONO_SIZEOF_DYN_CALL_RET_BUF) {
3198+
retval_malloc = TRUE;
3199+
retval_ptr = g_new0 (guint8, inst_size);
3200+
g_assert (retval_ptr);
3201+
}
3202+
}
3203+
if (!retval_malloc)
3204+
retval_ptr = &retval;
31883205
args [pindex ++] = &retval_ptr;
31893206
}
31903207
for (i = 0; i < sig->param_count; ++i) {
@@ -3234,7 +3251,10 @@ mono_llvmonly_runtime_invoke (MonoMethod *method, RuntimeInvokeInfo *info, void
32343251
if (sig->ret->byref) {
32353252
return mono_value_box_checked (info->ret_box_class, *(gpointer*)retval, error);
32363253
} else {
3237-
return mono_value_box_checked (info->ret_box_class, retval, error);
3254+
MonoObject *ret = mono_value_box_checked (info->ret_box_class, retval_ptr, error);
3255+
if (retval_malloc)
3256+
g_free (retval_ptr);
3257+
return ret;
32383258
}
32393259
} else {
32403260
if (sig->ret->byref)
@@ -3397,7 +3417,25 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
33973417
gpointer *args;
33983418
int i, pindex, buf_size;
33993419
guint8 *buf;
3400-
guint8 retval [256];
3420+
guint8 retbuf [MONO_SIZEOF_DYN_CALL_RET_BUF];
3421+
guint8 *retval = &retbuf[0];
3422+
gboolean retval_malloc = FALSE;
3423+
3424+
/* if the return value is too big, put it in a dynamically allocated temporary */
3425+
if (info->ret_box_class && !sig->ret->byref &&
3426+
(sig->ret->type == MONO_TYPE_VALUETYPE ||
3427+
(sig->ret->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (sig->ret)))) {
3428+
// if the return type is a struct and its too big for the stack buffer, malloc instead
3429+
MonoClass *ret_klass = mono_class_from_mono_type_internal (sig->ret);
3430+
g_assert (!mono_class_has_failure (ret_klass));
3431+
int32_t inst_size = mono_class_instance_size (ret_klass);
3432+
if (inst_size > MONO_SIZEOF_DYN_CALL_RET_BUF) {
3433+
retval_malloc = TRUE;
3434+
retval = g_new0 (guint8, inst_size);
3435+
g_assert (retval);
3436+
}
3437+
}
3438+
34013439

34023440
/* Convert the arguments to the format expected by start_dyn_call () */
34033441
args = (void **)g_alloca ((sig->param_count + sig->hasthis) * sizeof (gpointer));
@@ -3433,10 +3471,31 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
34333471
return NULL;
34343472
}
34353473

3436-
if (info->ret_box_class)
3437-
return mono_value_box_checked (info->ret_box_class, retval, error);
3438-
else
3439-
return *(MonoObject**)retval;
3474+
if (sig->ret->byref) {
3475+
if (*(gpointer*)retval == NULL) {
3476+
MonoClass *klass = mono_class_get_nullbyrefreturn_ex_class ();
3477+
MonoObject *ex = mono_object_new_checked (klass, error);
3478+
mono_error_assert_ok (error);
3479+
mono_error_set_exception_instance (error, (MonoException*)ex);
3480+
return NULL;
3481+
}
3482+
}
3483+
3484+
if (info->ret_box_class) {
3485+
if (sig->ret->byref) {
3486+
return mono_value_box_checked (info->ret_box_class, *(gpointer*)retval, error);
3487+
} else {
3488+
MonoObject *boxed_ret = mono_value_box_checked (info->ret_box_class, retval, error);
3489+
if (retval_malloc)
3490+
g_free (retval);
3491+
return boxed_ret;
3492+
}
3493+
} else {
3494+
if (sig->ret->byref)
3495+
return **(MonoObject***)retval;
3496+
else
3497+
return *(MonoObject**)retval;
3498+
}
34403499
}
34413500
#endif
34423501

0 commit comments

Comments
 (0)