Skip to content
Merged
Changes from 1 commit
Commits
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
Next Next commit
[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
  • Loading branch information
lambdageek committed Aug 27, 2021
commit ffa6cc9c6bc085f0a95a96962249509d7ff6ca5f
65 changes: 57 additions & 8 deletions src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -2998,6 +2998,8 @@ typedef struct {
gpointer *wrapper_arg;
} RuntimeInvokeInfo;

#define MONO_SIZEOF_DYN_CALL_RET_BUF 256

static RuntimeInvokeInfo*
create_runtime_invoke_info (MonoMethod *method, gpointer compiled_method, gboolean callee_gsharedvt, gboolean use_interp, MonoError *error)
{
Expand Down Expand Up @@ -3156,8 +3158,9 @@ mono_llvmonly_runtime_invoke (MonoMethod *method, RuntimeInvokeInfo *info, void
{
MonoMethodSignature *sig = info->sig;
MonoObject *(*runtime_invoke) (MonoObject *this_obj, void **params, MonoObject **exc, void* compiled_method);
gboolean retval_malloc = FALSE;
gpointer retval_ptr;
guint8 retval [256];
guint8 retval [MONO_SIZEOF_DYN_CALL_RET_BUF];
int i, pindex;

error_init (error);
Expand All @@ -3184,7 +3187,21 @@ mono_llvmonly_runtime_invoke (MonoMethod *method, RuntimeInvokeInfo *info, void
if (sig->hasthis)
args [pindex ++] = &obj;
if (sig->ret->type != MONO_TYPE_VOID) {
retval_ptr = &retval;
if (info->ret_box_class && !sig->ret->byref &&
(sig->ret->type == MONO_TYPE_VALUETYPE ||
(sig->ret->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (sig->ret)))) {
// if the return type is a struct and its too big for the stack buffer, malloc instead
MonoClass *ret_klass = mono_class_from_mono_type_internal (sig->ret);
g_assert (!mono_class_has_failure (ret_klass));
int32_t inst_size = mono_class_instance_size (ret_klass);
if (inst_size > MONO_SIZEOF_DYN_CALL_RET_BUF) {
retval_malloc = TRUE;
retval_ptr = g_new0 (guint8, inst_size);
g_assert (retval_ptr);
}
}
if (!retval_malloc)
retval_ptr = &retval;
args [pindex ++] = &retval_ptr;
}
for (i = 0; i < sig->param_count; ++i) {
Expand Down Expand Up @@ -3234,7 +3251,10 @@ mono_llvmonly_runtime_invoke (MonoMethod *method, RuntimeInvokeInfo *info, void
if (sig->ret->byref) {
return mono_value_box_checked (info->ret_box_class, *(gpointer*)retval, error);
} else {
return mono_value_box_checked (info->ret_box_class, retval, error);
MonoObject *ret = mono_value_box_checked (info->ret_box_class, retval_ptr, error);
if (retval_malloc)
g_free (retval_ptr);
return ret;
}
} else {
if (sig->ret->byref)
Expand Down Expand Up @@ -3397,7 +3417,25 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
gpointer *args;
int i, pindex, buf_size;
guint8 *buf;
guint8 retval [256];
guint8 retbuf [MONO_SIZEOF_DYN_CALL_RET_BUF];
guint8 *retval = &retbuf[0];
gboolean retval_malloc = FALSE;

/* if the return value is too big, put it in a dynamically allocated temporary */
if (info->ret_box_class && !sig->ret->byref &&
(sig->ret->type == MONO_TYPE_VALUETYPE ||
(sig->ret->type == MONO_TYPE_GENERICINST && !MONO_TYPE_IS_REFERENCE (sig->ret)))) {
// if the return type is a struct and its too big for the stack buffer, malloc instead
MonoClass *ret_klass = mono_class_from_mono_type_internal (sig->ret);
g_assert (!mono_class_has_failure (ret_klass));
int32_t inst_size = mono_class_instance_size (ret_klass);
if (inst_size > MONO_SIZEOF_DYN_CALL_RET_BUF) {
retval_malloc = TRUE;
retval = g_new0 (guint8, inst_size);
g_assert (retval);
}
}


/* Convert the arguments to the format expected by start_dyn_call () */
args = (void **)g_alloca ((sig->param_count + sig->hasthis) * sizeof (gpointer));
Expand Down Expand Up @@ -3433,10 +3471,21 @@ mono_jit_runtime_invoke (MonoMethod *method, void *obj, void **params, MonoObjec
return NULL;
}

if (info->ret_box_class)
return mono_value_box_checked (info->ret_box_class, retval, error);
else
return *(MonoObject**)retval;
if (info->ret_box_class) {
if (sig->ret->byref) {
return mono_value_box_checked (info->ret_box_class, *(gpointer*)retval, error);
} else {
MonoObject *boxed_ret = mono_value_box_checked (info->ret_box_class, retval, error);
if (retval_malloc)
g_free (retval);
return boxed_ret;
}
} else {
if (sig->ret->byref)
return **(MonoObject***)retval;
else
return *(MonoObject**)retval;
}
}
#endif

Expand Down