diff --git a/src/mono/mono/mini/interp/interp.c b/src/mono/mono/mini/interp/interp.c index 68c301ea889a06..f3cc76774ce8ea 100644 --- a/src/mono/mono/mini/interp/interp.c +++ b/src/mono/mono/mini/interp/interp.c @@ -985,6 +985,48 @@ stackval_to_data (MonoType *type, stackval *val, void *data, gboolean pinvoke) } } +static void +stackval_to_data_sign_ext (MonoType *type, stackval *val, void *data, gboolean pinvoke) +{ + switch (type->type) { + case MONO_TYPE_I1: { + mono_i *p = (mono_i*)data; + *p = (mono_i)((gint8)val->data.i); + break; + } + case MONO_TYPE_U1: { + mono_u *p = (mono_u*)data; + *p = (mono_u)((guint8)val->data.i); + break; + } + case MONO_TYPE_I2: { + mono_i *p = (mono_i*)data; + *p = (mono_i)((gint16)val->data.i); + break; + } + case MONO_TYPE_U2: { + mono_u *p = (mono_u*)data; + *p = (mono_u)((guint16)val->data.i); + break; + } +#if SIZEOF_VOID_P == 8 + case MONO_TYPE_I4: { + mono_i *p = (mono_i*)data; + *p = (mono_i)(val->data.i); + break; + } + case MONO_TYPE_U4: { + mono_u *p = (mono_u*)data; + *p = (mono_u)((guint32)val->data.i); + break; + } +#endif + default: + stackval_to_data (type, val, data, pinvoke); + break; + } +} + typedef struct { MonoException *ex; MonoContext *ctx; @@ -1651,7 +1693,7 @@ interp_frame_arg_to_data (MonoInterpFrameHandle frame, MonoMethodSignature *sig, // If index == -1, we finished executing an InterpFrame and the result is at retval. if (index == -1) - stackval_to_data (sig->ret, iframe->retval, data, sig->pinvoke && !sig->marshalling_disabled); + stackval_to_data_sign_ext (sig->ret, iframe->retval, data, sig->pinvoke && !sig->marshalling_disabled); else if (sig->hasthis && index == 0) *(gpointer*)data = iframe->stack->data.p; else @@ -2353,8 +2395,17 @@ interp_entry (InterpEntryData *data) // The return value is at the bottom of the stack, after the locals space type = rmethod->rtype; - if (type->type != MONO_TYPE_VOID) - stackval_to_data (type, frame.stack, data->res, FALSE); + if (type->type != MONO_TYPE_VOID) { + // interp entry is called either from a interp_in wrapper or a gsharedvt_in_sig wrapper + // interp_in wrappers always return intptr (they are more aggresively shared) while the + // gsharedvt_in_sig wrapper returns the actual type. This check follows the logic in + // interp_create_method_pointer_llvmonly and interp_create_method_pointer so we do the + // return sign extension only when called from the interp_in wrapper. + if (!mono_llvm_only || sig->param_count > MAX_INTERP_ENTRY_ARGS) + stackval_to_data_sign_ext (type, frame.stack, data->res, FALSE); + else + stackval_to_data (type, frame.stack, data->res, FALSE); + } } static void