Skip to content

Commit

Permalink
[interp] fullAOT mixed mode (#8838)
Browse files Browse the repository at this point in the history
* [jit] Move the handling of special methods like pinvoke etc. into mini-runtime.c, since they are not handled by the JIT.

* [jit] Add a --full-aot-interp command line argument which is the same as --full-aot with a fallback to the interpreter.

* [jit] Implement fallback to the interpreter in full-aot+interp mode.

* [jit] Add a 'methods using the interpreter' stat.
  • Loading branch information
vargaz authored and marek-safar committed May 24, 2018
1 parent 319b410 commit c06c9b7
Show file tree
Hide file tree
Showing 6 changed files with 178 additions and 158 deletions.
3 changes: 3 additions & 0 deletions man/mono.1
Expand Up @@ -478,6 +478,9 @@ in your program, or Mono's class libraries attempts to generate code
dynamically. You should test your software upfront and make sure that
you do not use any dynamic features.
.TP
\fB--full-aot-interp\fR
Same as --full-aot with fallback to the interpreter.
.TP
\fB--gc=boehm\fR, \fB--gc=sgen\fR
Selects the Garbage Collector engine for Mono to use, Boehm or SGen.
Currently this merely ensures that you are running either the
Expand Down
2 changes: 2 additions & 0 deletions mono/mini/driver.c
Expand Up @@ -1987,6 +1987,8 @@ mono_main (int argc, char* argv[])
mono_jit_set_aot_mode (MONO_AOT_MODE_LLVMONLY);
} else if (strcmp (argv [i], "--hybrid-aot") == 0) {
mono_jit_set_aot_mode (MONO_AOT_MODE_HYBRID);
} else if (strcmp (argv [i], "--full-aot-interp") == 0) {
mono_jit_set_aot_mode (MONO_AOT_MODE_INTERP);
} else if (strcmp (argv [i], "--print-vtable") == 0) {
mono_print_vtable = TRUE;
} else if (strcmp (argv [i], "--stats") == 0) {
Expand Down
1 change: 1 addition & 0 deletions mono/mini/interp/transform.c
Expand Up @@ -4940,6 +4940,7 @@ mono_interp_transform_method (InterpMethod *imethod, ThreadContext *context, Int
imethod->next_jit_code_hash = hash;
mono_memory_barrier ();
imethod->transformed = TRUE;
mono_atomic_fetch_add_i32 (&mono_jit_stats.methods_with_interp, 1);
}
mono_os_mutex_unlock (&calc_section);

Expand Down
184 changes: 171 additions & 13 deletions mono/mini/mini-runtime.c
Expand Up @@ -2077,6 +2077,163 @@ unregister_method_for_compile (MonoMethod *method, MonoDomain *target_domain)
unlock_compilation_data ();
}

static MonoJitInfo*
create_jit_info_for_trampoline (MonoMethod *wrapper, MonoTrampInfo *info)
{
MonoDomain *domain = mono_get_root_domain ();
MonoJitInfo *jinfo;
guint8 *uw_info;
guint32 info_len;

if (info->uw_info) {
uw_info = info->uw_info;
info_len = info->uw_info_len;
} else {
uw_info = mono_unwind_ops_encode (info->unwind_ops, &info_len);
}

jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, MONO_SIZEOF_JIT_INFO);
jinfo->d.method = wrapper;
jinfo->code_start = info->code;
jinfo->code_size = info->code_size;
jinfo->unwind_info = mono_cache_unwind_info (uw_info, info_len);

if (!info->uw_info)
g_free (uw_info);

return jinfo;
}

static gpointer
compile_special (MonoMethod *method, MonoDomain *target_domain, MonoError *error)
{
MonoJitInfo *jinfo;
gpointer code;

if (mono_llvm_only) {
if (method->wrapper_type == MONO_WRAPPER_UNKNOWN) {
WrapperInfo *info = mono_marshal_get_wrapper_info (method);

if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) {
/*
* These wrappers are only created for signatures which are in the program, but
* sometimes we load methods too eagerly and have to create them even if they
* will never be called.
*/
return no_gsharedvt_in_wrapper;
}
}
}

if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
MonoMethod *nm;
MonoMethodPInvoke* piinfo = (MonoMethodPInvoke *) method;

if (!piinfo->addr) {
if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
piinfo->addr = mono_lookup_internal_call (method);
else if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)
#ifdef HOST_WIN32
g_warning ("Method '%s' in assembly '%s' contains native code that cannot be executed by Mono in modules loaded from byte arrays. The assembly was probably created using C++/CLI.\n", mono_method_full_name (method, TRUE), m_class_get_image (method->klass)->name);
#else
g_warning ("Method '%s' in assembly '%s' contains native code that cannot be executed by Mono on this platform. The assembly was probably created using C++/CLI.\n", mono_method_full_name (method, TRUE), m_class_get_image (method->klass)->name);
#endif
else
mono_lookup_pinvoke_call (method, NULL, NULL);
}
nm = mono_marshal_get_native_wrapper (method, TRUE, mono_aot_only);
gpointer compiled_method = mono_compile_method_checked (nm, error);
return_val_if_nok (error, NULL);
code = mono_get_addr_from_ftnptr (compiled_method);
jinfo = mono_jit_info_table_find (target_domain, code);
if (!jinfo)
jinfo = mono_jit_info_table_find (mono_domain_get (), code);
if (jinfo)
MONO_PROFILER_RAISE (jit_done, (method, jinfo));
return code;
} else if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
const char *name = method->name;
char *full_name;
MonoMethod *nm;

if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("ves_icall_mono_delegate_ctor");
g_assert (mi);
/*
* We need to make sure this wrapper
* is compiled because it might end up
* in an (M)RGCTX if generic sharing
* is enabled, and would be called
* indirectly. If it were a
* trampoline we'd try to patch that
* indirect call, which is not
* possible.
*/
return mono_get_addr_from_ftnptr ((gpointer)mono_icall_get_wrapper_full (mi, TRUE));
} else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
if (mono_llvm_only) {
nm = mono_marshal_get_delegate_invoke (method, NULL);
gpointer compiled_ptr = mono_compile_method_checked (nm, error);
mono_error_assert_ok (error);
return mono_get_addr_from_ftnptr (compiled_ptr);
}
return mono_create_delegate_trampoline (target_domain, method->klass);
} else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
nm = mono_marshal_get_delegate_begin_invoke (method);
gpointer compiled_ptr = mono_compile_method_checked (nm, error);
mono_error_assert_ok (error);
return mono_get_addr_from_ftnptr (compiled_ptr);
} else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
nm = mono_marshal_get_delegate_end_invoke (method);
gpointer compiled_ptr = mono_compile_method_checked (nm, error);
mono_error_assert_ok (error);
return mono_get_addr_from_ftnptr (compiled_ptr);
}
}

full_name = mono_method_full_name (method, TRUE);
mono_error_set_invalid_program (error, "Unrecognizable runtime implemented method '%s'", full_name);
g_free (full_name);
return NULL;
}

if (method->wrapper_type == MONO_WRAPPER_UNKNOWN) {
WrapperInfo *info = mono_marshal_get_wrapper_info (method);

if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN || info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT) {
static MonoTrampInfo *in_tinfo, *out_tinfo;
MonoTrampInfo *tinfo;
MonoJitInfo *jinfo;
gboolean is_in = info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN;

if (is_in && in_tinfo)
return in_tinfo->code;
else if (!is_in && out_tinfo)
return out_tinfo->code;

/*
* This is a special wrapper whose body is implemented in assembly, like a trampoline. We use a wrapper so EH
* works.
* FIXME: The caller signature doesn't match the callee, which might cause problems on some platforms
*/
if (mono_ee_features.use_aot_trampolines)
mono_aot_get_trampoline_full (is_in ? "gsharedvt_trampoline" : "gsharedvt_out_trampoline", &tinfo);
else
mono_arch_get_gsharedvt_trampoline (&tinfo, FALSE);
jinfo = create_jit_info_for_trampoline (method, tinfo);
mono_jit_info_table_add (mono_get_root_domain (), jinfo);
if (is_in)
in_tinfo = tinfo;
else
out_tinfo = tinfo;
return tinfo->code;
}
}

return NULL;
}

static gpointer
mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_only, MonoError *error)
Expand Down Expand Up @@ -2211,26 +2368,26 @@ mono_jit_compile_method_with_opt (MonoMethod *method, guint32 opt, gboolean jit_
}
#endif

if (!code && mono_llvm_only) {
if (method->wrapper_type == MONO_WRAPPER_UNKNOWN) {
WrapperInfo *info = mono_marshal_get_wrapper_info (method);
if (!code)
code = compile_special (method, target_domain, error);

if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN_SIG) {
/*
* These wrappers are only created for signatures which are in the program, but
* sometimes we load methods too eagerly and have to create them even if they
* will never be called.
*/
return no_gsharedvt_in_wrapper;
}
}
}
if (!code && mono_aot_only && mono_use_interpreter && method->wrapper_type != MONO_WRAPPER_UNKNOWN)
code = mini_get_interp_callbacks ()->create_method_pointer (method, error);

if (!code) {
if (mono_class_is_open_constructed_type (m_class_get_byval_arg (method->klass))) {
mono_error_set_invalid_operation (error, "Could not execute the method because the containing type is not fully instantiated.");
return NULL;
}

if (mono_aot_only) {
char *fullname = mono_method_full_name (method, TRUE);
mono_error_set_execution_engine (error, "Attempting to JIT compile method '%s' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.\n", fullname);
g_free (fullname);

return NULL;
}

if (wait_or_register_method_to_compile (method, target_domain))
goto lookup_start;
code = mono_jit_compile_method_inner (method, target_domain, opt, error);
Expand Down Expand Up @@ -3662,6 +3819,7 @@ register_jit_stats (void)
mono_counters_register ("Methods from AOT", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.methods_aot);
mono_counters_register ("Methods JITted using mono JIT", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.methods_without_llvm);
mono_counters_register ("Methods JITted using LLVM", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.methods_with_llvm);
mono_counters_register ("Methods using the interpreter", MONO_COUNTER_JIT | MONO_COUNTER_INT, &mono_jit_stats.methods_with_interp);
mono_counters_register ("JIT/method_to_ir (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_method_to_ir);
mono_counters_register ("JIT/liveness_handle_exception_clauses (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_liveness_handle_exception_clauses);
mono_counters_register ("JIT/handle_out_of_line_bblock (sec)", MONO_COUNTER_JIT | MONO_COUNTER_DOUBLE, &mono_jit_stats.jit_handle_out_of_line_bblock);
Expand Down
145 changes: 0 additions & 145 deletions mono/mini/mini.c
Expand Up @@ -3965,33 +3965,6 @@ mono_cfg_set_exception_invalid_program (MonoCompile *cfg, char *msg)

#endif /* DISABLE_JIT */

static MonoJitInfo*
create_jit_info_for_trampoline (MonoMethod *wrapper, MonoTrampInfo *info)
{
MonoDomain *domain = mono_get_root_domain ();
MonoJitInfo *jinfo;
guint8 *uw_info;
guint32 info_len;

if (info->uw_info) {
uw_info = info->uw_info;
info_len = info->uw_info_len;
} else {
uw_info = mono_unwind_ops_encode (info->unwind_ops, &info_len);
}

jinfo = (MonoJitInfo *)mono_domain_alloc0 (domain, MONO_SIZEOF_JIT_INFO);
jinfo->d.method = wrapper;
jinfo->code_start = info->code;
jinfo->code_size = info->code_size;
jinfo->unwind_info = mono_cache_unwind_info (uw_info, info_len);

if (!info->uw_info)
g_free (uw_info);

return jinfo;
}

GTimer *mono_time_track_start ()
{
return g_timer_new ();
Expand Down Expand Up @@ -4047,124 +4020,6 @@ mono_jit_compile_method_inner (MonoMethod *method, MonoDomain *target_domain, in

error_init (error);

if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) ||
(method->flags & METHOD_ATTRIBUTE_PINVOKE_IMPL)) {
MonoMethod *nm;
MonoMethodPInvoke* piinfo = (MonoMethodPInvoke *) method;

if (!piinfo->addr) {
if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL)
piinfo->addr = mono_lookup_internal_call (method);
else if (method->iflags & METHOD_IMPL_ATTRIBUTE_NATIVE)
#ifdef HOST_WIN32
g_warning ("Method '%s' in assembly '%s' contains native code that cannot be executed by Mono in modules loaded from byte arrays. The assembly was probably created using C++/CLI.\n", mono_method_full_name (method, TRUE), m_class_get_image (method->klass)->name);
#else
g_warning ("Method '%s' in assembly '%s' contains native code that cannot be executed by Mono on this platform. The assembly was probably created using C++/CLI.\n", mono_method_full_name (method, TRUE), m_class_get_image (method->klass)->name);
#endif
else
mono_lookup_pinvoke_call (method, NULL, NULL);
}
nm = mono_marshal_get_native_wrapper (method, TRUE, mono_aot_only);
gpointer compiled_method = mono_compile_method_checked (nm, error);
return_val_if_nok (error, NULL);
code = mono_get_addr_from_ftnptr (compiled_method);
jinfo = mono_jit_info_table_find (target_domain, code);
if (!jinfo)
jinfo = mono_jit_info_table_find (mono_domain_get (), code);
if (jinfo)
MONO_PROFILER_RAISE (jit_done, (method, jinfo));
return code;
} else if ((method->iflags & METHOD_IMPL_ATTRIBUTE_RUNTIME)) {
const char *name = method->name;
char *full_name, *msg;
MonoMethod *nm;

if (m_class_get_parent (method->klass) == mono_defaults.multicastdelegate_class) {
if (*name == '.' && (strcmp (name, ".ctor") == 0)) {
MonoJitICallInfo *mi = mono_find_jit_icall_by_name ("ves_icall_mono_delegate_ctor");
g_assert (mi);
/*
* We need to make sure this wrapper
* is compiled because it might end up
* in an (M)RGCTX if generic sharing
* is enabled, and would be called
* indirectly. If it were a
* trampoline we'd try to patch that
* indirect call, which is not
* possible.
*/
return mono_get_addr_from_ftnptr ((gpointer)mono_icall_get_wrapper_full (mi, TRUE));
} else if (*name == 'I' && (strcmp (name, "Invoke") == 0)) {
if (mono_llvm_only) {
nm = mono_marshal_get_delegate_invoke (method, NULL);
gpointer compiled_ptr = mono_compile_method_checked (nm, error);
mono_error_assert_ok (error);
return mono_get_addr_from_ftnptr (compiled_ptr);
}
return mono_create_delegate_trampoline (target_domain, method->klass);
} else if (*name == 'B' && (strcmp (name, "BeginInvoke") == 0)) {
nm = mono_marshal_get_delegate_begin_invoke (method);
gpointer compiled_ptr = mono_compile_method_checked (nm, error);
mono_error_assert_ok (error);
return mono_get_addr_from_ftnptr (compiled_ptr);
} else if (*name == 'E' && (strcmp (name, "EndInvoke") == 0)) {
nm = mono_marshal_get_delegate_end_invoke (method);
gpointer compiled_ptr = mono_compile_method_checked (nm, error);
mono_error_assert_ok (error);
return mono_get_addr_from_ftnptr (compiled_ptr);
}
}

full_name = mono_method_full_name (method, TRUE);
msg = g_strdup_printf ("Unrecognizable runtime implemented method '%s'", full_name);
ex = mono_exception_from_name_msg (mono_defaults.corlib, "System", "InvalidProgramException", msg);
mono_error_set_exception_instance (error, ex);
g_free (full_name);
g_free (msg);
return NULL;
}

if (method->wrapper_type == MONO_WRAPPER_UNKNOWN) {
WrapperInfo *info = mono_marshal_get_wrapper_info (method);

if (info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN || info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_OUT) {
static MonoTrampInfo *in_tinfo, *out_tinfo;
MonoTrampInfo *tinfo;
MonoJitInfo *jinfo;
gboolean is_in = info->subtype == WRAPPER_SUBTYPE_GSHAREDVT_IN;

if (is_in && in_tinfo)
return in_tinfo->code;
else if (!is_in && out_tinfo)
return out_tinfo->code;

/*
* This is a special wrapper whose body is implemented in assembly, like a trampoline. We use a wrapper so EH
* works.
* FIXME: The caller signature doesn't match the callee, which might cause problems on some platforms
*/
if (mono_ee_features.use_aot_trampolines)
mono_aot_get_trampoline_full (is_in ? "gsharedvt_trampoline" : "gsharedvt_out_trampoline", &tinfo);
else
mono_arch_get_gsharedvt_trampoline (&tinfo, FALSE);
jinfo = create_jit_info_for_trampoline (method, tinfo);
mono_jit_info_table_add (mono_get_root_domain (), jinfo);
if (is_in)
in_tinfo = tinfo;
else
out_tinfo = tinfo;
return tinfo->code;
}
}

if (mono_aot_only) {
char *fullname = mono_method_full_name (method, TRUE);
mono_error_set_execution_engine (error, "Attempting to JIT compile method '%s' while running in aot-only mode. See https://docs.microsoft.com/xamarin/ios/internals/limitations for more information.\n", fullname);
g_free (fullname);

return NULL;
}

jit_timer = mono_time_track_start ();
cfg = mini_method_compile (method, opt, target_domain, JIT_FLAG_RUN_CCTORS, 0, -1);
gdouble jit_time = 0.0;
Expand Down

0 comments on commit c06c9b7

Please sign in to comment.