Add support to register an external icall getting minimal runtime overhead when called. An icall that can be used without a wrapper needs to follow specific rules in order to correctly collaborate with runtime. All functions are currently internal only and only accessible by runtime or embedders using static linking and fully acknowledge that this is not an optimization available as a public API.
Co-authored-by: lateralusX <lateralusX@users.noreply.github.com>
MonoMethod method;
gpointer addr;
/* add marshal info */
- guint16 piflags; /* pinvoke flags */
+ union {
+ guint16 piflags; /* pinvoke flags */
+ guint16 icflags; /* icall flags */
+ };
guint32 implmap_idx; /* index into IMPLMAP */
};
MONO_ICALL_FLAGS_NONE = 0,
MONO_ICALL_FLAGS_FOREIGN = 1 << 1,
MONO_ICALL_FLAGS_USES_HANDLES = 1 << 2,
- MONO_ICALL_FLAGS_COOPERATIVE = 1 << 3
+ MONO_ICALL_FLAGS_COOPERATIVE = 1 << 3,
+ MONO_ICALL_FLAGS_NO_WRAPPER = 1 << 4
} MonoInternalCallFlags;
gconstpointer
mono_lookup_internal_call_full_with_flags (MonoMethod *method, gboolean warn_on_missing, guint32 *flags);
+void
+mono_dangerous_add_internal_call_coop (const char *name, const void* method);
+
+void
+mono_dangerous_add_internal_call_no_wrapper (const char *name, const void* method);
+
gboolean
mono_is_missing_icall_addr (gconstpointer addr);
}
/**
+* mono_dangerous_add_internal_call_coop:
+* \param name method specification to surface to the managed world
+* \param method pointer to a C method to invoke when the method is called
+*
+* Similar to \c mono_dangerous_add_raw_internal_call.
+*
+*/
+void
+mono_dangerous_add_internal_call_coop (const char *name, gconstpointer method)
+{
+ add_internal_call_with_flags (name, method, MONO_ICALL_FLAGS_COOPERATIVE);
+}
+
+/**
+* mono_dangerous_add_internal_call_no_wrapper:
+* \param name method specification to surface to the managed world
+* \param method pointer to a C method to invoke when the method is called
+*
+* Similar to \c mono_dangerous_add_raw_internal_call but with more requirements for correct
+* operation.
+*
+* The \p method must NOT:
+*
+* Run for an unbounded amount of time without calling the mono runtime.
+* Additionally, the method must switch to GC Safe mode to perform all blocking
+* operations: performing blocking I/O, taking locks, etc. The method can't throw or raise
+* exceptions or call other methods that will throw or raise exceptions since the runtime won't
+* be able to detect exeptions and unwinder won't be able to correctly find last managed frame in callstack.
+* This registration method is for icalls that needs very low overhead and follow all rules in their implementation.
+*
+*/
+void
+mono_dangerous_add_internal_call_no_wrapper (const char *name, gconstpointer method)
+{
+ add_internal_call_with_flags (name, method, MONO_ICALL_FLAGS_NO_WRAPPER);
+}
+
+/**
* mono_add_internal_call:
* \param name method specification to surface to the managed world
* \param method pointer to a C method to invoke when the method is called
void
mono_add_internal_call (const char *name, gconstpointer method)
{
- mono_add_internal_call_with_flags (name, method, FALSE);
+ add_internal_call_with_flags (name, method, MONO_ICALL_FLAGS_FOREIGN);
}
/**
void
mono_dangerous_add_raw_internal_call (const char *name, gconstpointer method)
{
- mono_add_internal_call_with_flags (name, method, TRUE);
+ add_internal_call_with_flags (name, method, MONO_ICALL_FLAGS_COOPERATIVE);
}
/**
void
mono_add_internal_call_internal (const char *name, gconstpointer method)
{
- mono_add_internal_call_with_flags (name, method, TRUE);
+ add_internal_call_with_flags (name, method, MONO_ICALL_FLAGS_COOPERATIVE);
}
/*
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)
+ if (method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) {
+ guint32 flags = MONO_ICALL_FLAGS_NONE;
+ gpointer icall_addr;
+ icall_addr = (gpointer)mono_lookup_internal_call_full_with_flags (method, TRUE, (guint32 *)&flags);
+ if (flags & MONO_ICALL_FLAGS_NO_WRAPPER) {
+ piinfo->icflags = MONO_ICALL_FLAGS_NO_WRAPPER;
+ mono_memory_write_barrier ();
+ }
+ piinfo->addr = icall_addr;
+ } 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 {
+ } else {
ERROR_DECL (ignored_error);
mono_lookup_pinvoke_call_internal (method, ignored_error);
mono_error_cleanup (ignored_error);
}
}
- MonoMethod *nm = mono_marshal_get_native_wrapper (method, TRUE, mono_aot_only);
- gpointer compiled_method = mono_jit_compile_method_jit_only (nm, error);
- return_val_if_nok (error, NULL);
+ mono_memory_read_barrier ();
+
+ gpointer compiled_method = NULL;
+ if ((method->iflags & METHOD_IMPL_ATTRIBUTE_INTERNAL_CALL) && (piinfo->icflags & MONO_ICALL_FLAGS_NO_WRAPPER)) {
+ compiled_method = piinfo->addr;
+ } else {
+ MonoMethod *nm = mono_marshal_get_native_wrapper (method, TRUE, mono_aot_only);
+ compiled_method = mono_jit_compile_method_jit_only (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);