Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[mono][aot] Add support for deferred failures during AOT compilation #86554

Merged
merged 8 commits into from
May 31, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
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
17 changes: 17 additions & 0 deletions src/mono/mono/metadata/class-accessors.c
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,23 @@ mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error)
return TRUE;
}

/**
* mono_class_set_deferred_failure:
* \param klass class in which the failure was detected

* This method marks the class with a deferred failure, indicating that a failure was detected but it will be processed during AOT runtime..
* Note that only the first failure is kept.
*
* LOCKING: Acquires the loader lock.
*/
void
mono_class_set_deferred_failure (MonoClass *klass)
{
mono_loader_lock ();
klass->has_deferred_failure = 1;
mono_loader_unlock ();
}

/**
* mono_class_set_nonblittable:
* \param klass class which will be marked as not blittable.
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/class-getters.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ MONO_CLASS_GETTER(m_class_is_simd_type, gboolean, , MonoClass, simd_type)
MONO_CLASS_GETTER(m_class_is_has_finalize_inited, gboolean, , MonoClass, has_finalize_inited)
MONO_CLASS_GETTER(m_class_is_fields_inited, gboolean, , MonoClass, fields_inited)
MONO_CLASS_GETTER(m_class_has_failure, gboolean, , MonoClass, has_failure)
MONO_CLASS_GETTER(m_class_has_deferred_failure, gboolean, , MonoClass, has_deferred_failure)
MONO_CLASS_GETTER(m_class_has_weak_fields, gboolean, , MonoClass, has_weak_fields)
MONO_CLASS_GETTER(m_class_has_dim_conflicts, gboolean, , MonoClass, has_dim_conflicts)
MONO_CLASS_GETTER(m_class_get_parent, MonoClass *, , MonoClass, parent)
Expand Down
23 changes: 17 additions & 6 deletions src/mono/mono/metadata/class-init.c
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ mono_class_setup_fields (MonoClass *klass)
}

if (m_class_is_inlinearray (klass) && m_class_inlinearray_value (klass) <= 0)
mono_class_set_type_load_failure (klass, "Inline array length property must be positive.");
type_load_failure_callback (klass, "Inline array length property must be positive.");
kotlarmilos marked this conversation as resolved.
Show resolved Hide resolved

/* Get the real size */
explicit_size = mono_metadata_packing_from_typedef (klass->image, klass->type_token, &packing_size, &real_size);
Expand Down Expand Up @@ -376,8 +376,8 @@ mono_class_setup_fields (MonoClass *klass)
break;
}
if (m_class_is_inlinearray (klass)) {
mono_class_set_type_load_failure (klass, "Inline array struct must not have explicit layout.");
break;
if (type_load_failure_callback (klass, "Inline array struct must not have explicit layout."))
break;
}
}
if (mono_type_has_exceptions (field->type)) {
Expand Down Expand Up @@ -2274,12 +2274,15 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
if (m_class_is_inlinearray (klass)) {
// Limit the max size of array instance to 1MiB
const guint32 struct_max_size = 1024 * 1024;
guint32 initial_size = size;
// If size overflows, it returns 0
size *= m_class_inlinearray_value (klass);
inlined_fields++;
if(size == 0 || size > struct_max_size) {
mono_class_set_type_load_failure (klass, "Inline array struct size out of bounds, abnormally large.");
break;
if (type_load_failure_callback (klass, "Inline array struct size out of bounds, abnormally large."))
break;
else
size = initial_size;
}
}

Expand Down Expand Up @@ -2310,7 +2313,7 @@ mono_class_layout_fields (MonoClass *klass, int base_instance_size, int packing_
}
}
if (m_class_is_inlinearray (klass) && inlined_fields != 1)
mono_class_set_type_load_failure (klass, "Inline array struct must have a single field.");
type_load_failure_callback (klass, "Inline array struct must have a single field.");
break;
case TYPE_ATTRIBUTE_EXPLICIT_LAYOUT: {
real_size = 0;
Expand Down Expand Up @@ -2991,6 +2994,14 @@ mono_class_init_internal (MonoClass *klass)

has_cached_info = mono_class_get_cached_class_info (klass, &cached_info);

/*
* If the class has a deferred failure, ignore the cached info and
* let the runtime go on the slow path of trying to setup the class
* layout at runtime.
*/
if (has_cached_info && cached_info.has_deferred_failure)
has_cached_info = FALSE;

/* Compute instance size etc. */
init_sizes_with_info (klass, has_cached_info ? &cached_info : NULL);
if (mono_class_has_failure (klass))
Expand Down
10 changes: 7 additions & 3 deletions src/mono/mono/metadata/class-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,7 @@ typedef struct MonoCachedClassInfo {
guint no_special_static_fields : 1;
guint is_generic_container : 1;
guint has_weak_fields : 1;
guint has_deferred_failure : 1;
guint32 cctor_token;
MonoImage *finalize_image;
guint32 finalize_token;
Expand Down Expand Up @@ -1062,9 +1063,6 @@ mono_register_jit_icall_info (MonoJitICallInfo *info, T func, const char *name,

#define mono_register_jit_icall(func, sig, no_wrapper) (mono_register_jit_icall_info (&mono_get_jit_icall_info ()->func, func, #func, (sig), (no_wrapper), NULL))

gboolean
mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

MonoException*
mono_class_get_exception_for_failure (MonoClass *klass);

Expand Down Expand Up @@ -1268,6 +1266,9 @@ mono_error_set_for_class_failure (MonoError *orerror, const MonoClass *klass);
gboolean
mono_class_has_failure (const MonoClass *klass);

gboolean
mono_class_has_deferred_failure (const MonoClass *klass);

/* Kind specific accessors */
MONO_COMPONENT_API MonoGenericClass*
mono_class_get_generic_class (MonoClass *klass);
Expand Down Expand Up @@ -1429,6 +1430,9 @@ mono_class_find_enum_basetype (MonoClass *klass, MonoError *error);
gboolean
mono_class_set_failure (MonoClass *klass, MonoErrorBoxed *boxed_error);

void
mono_class_set_deferred_failure (MonoClass *klass);

gboolean
mono_class_set_type_load_failure_causedby_class (MonoClass *klass, const MonoClass *caused_by, const gchar* msg);

Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/metadata/class-private-definition.h
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ struct _MonoClass {
guint has_weak_fields : 1; /* class has weak reference fields */
guint has_dim_conflicts : 1; /* Class has conflicting default interface methods */
guint any_field_has_auto_layout : 1; /* a field in this type's layout uses auto-layout */
guint has_deferred_failure : 1;

MonoClass *parent;
MonoClass *nested_in;
Expand Down
31 changes: 3 additions & 28 deletions src/mono/mono/metadata/class.c
Original file line number Diff line number Diff line change
Expand Up @@ -5943,36 +5943,11 @@ mono_class_has_failure (const MonoClass *klass)
return m_class_has_failure ((MonoClass*)klass) != 0;
}


/**
* mono_class_set_type_load_failure:
* \param klass class in which the failure was detected
* \param fmt \c printf -style error message string.
*
* Collect detected failure informaion in the class for later processing.
* The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class()
* Note that only the first failure is kept.
*
* LOCKING: Acquires the loader lock.
*
* \returns FALSE if a failure was already set on the class, or TRUE otherwise.
*/
gboolean
mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...)
mono_class_has_deferred_failure (const MonoClass *klass)
{
ERROR_DECL (prepare_error);
va_list args;

if (mono_class_has_failure (klass))
return FALSE;

va_start (args, fmt);
mono_error_vset_type_load_class (prepare_error, klass, fmt, args);
va_end (args);

MonoErrorBoxed *box = mono_error_box (prepare_error, m_class_get_image (klass));
mono_error_cleanup (prepare_error);
return mono_class_set_failure (klass, box);
g_assert (klass != NULL);
return m_class_has_deferred_failure ((MonoClass*)klass) != 0;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/mono/mono/mini/aot-compiler.c
Original file line number Diff line number Diff line change
Expand Up @@ -7816,7 +7816,7 @@ emit_klass_info (MonoAotCompile *acfg, guint32 token)
} else {
gboolean has_nested = mono_class_get_nested_classes_property (klass) != NULL;
encode_value (m_class_get_vtable_size (klass), p, &p);
encode_value ((m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p);
encode_value ((m_class_has_deferred_failure (klass) << 10) | (m_class_has_weak_fields (klass) << 9) | (mono_class_is_gtd (klass) ? (1 << 8) : 0) | (no_special_static << 7) | (m_class_has_static_refs (klass) << 6) | (m_class_has_references (klass) << 5) | ((m_class_is_blittable (klass) << 4) | (has_nested ? 1 : 0) << 3) | (m_class_has_cctor (klass) << 2) | (m_class_has_finalize (klass) << 1) | m_class_is_ghcimpl (klass), p, &p);
if (m_class_has_cctor (klass))
encode_method_ref (acfg, mono_class_get_cctor (klass), p, &p);
if (m_class_has_finalize (klass))
Expand Down
1 change: 1 addition & 0 deletions src/mono/mono/mini/aot-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -2498,6 +2498,7 @@ decode_cached_class_info (MonoAotModule *module, MonoCachedClassInfo *info, guin
info->no_special_static_fields = (flags >> 7) & 0x1;
info->is_generic_container = (flags >> 8) & 0x1;
info->has_weak_fields = (flags >> 9) & 0x1;
info->has_deferred_failure = (flags >> 10) & 0x1;

if (info->has_cctor) {
res = decode_method_ref (module, &ref, buf, &buf, error);
Expand Down
3 changes: 2 additions & 1 deletion src/mono/mono/mini/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -1400,7 +1400,7 @@ main_thread_handler (gpointer user_data)
MonoAssembly **assemblies;

assemblies = g_new0 (MonoAssembly*, main_args->argc);

set_failure_type (DEFERRED_FAILURE);
/* Treat the other arguments as assemblies to compile too */
for (i = 0; i < main_args->argc; ++i) {
assembly = mono_domain_assembly_open_internal (mono_alc_get_default (), main_args->argv [i]);
Expand Down Expand Up @@ -1428,6 +1428,7 @@ main_thread_handler (gpointer user_data)
return;
}

set_failure_type (IMMEDIATE_FAILURE);
assembly = mono_domain_assembly_open_internal (mono_alc_get_default (), main_args->file);
if (!assembly){
fprintf (stderr, "Can not open image %s\n", main_args->file);
Expand Down
1 change: 0 additions & 1 deletion src/mono/mono/mini/mini-runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -1547,7 +1547,6 @@ mono_resolve_patch_target_ext (MonoMemoryManager *mem_manager, MonoMethod *metho
break;
case MONO_PATCH_INFO_VTABLE:
target = mono_class_vtable_checked (patch_info->data.klass, error);
mono_error_assert_ok (error);
break;
case MONO_PATCH_INFO_DELEGATE_INFO: {
MonoDelegateClassMethodPair *del_tramp = patch_info->data.del_tramp;
Expand Down
30 changes: 30 additions & 0 deletions src/mono/mono/utils/mono-error-internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,36 @@ mono_error_set_specific (MonoError *error, int error_code, const char *missing_m
void
mono_error_set_first_argument (MonoError *oerror, const char *first_argument);

typedef enum {
DEFERRED_FAILURE, // Used during AOT compilation to defer failure for execution
IMMEDIATE_FAILURE // Used during runtime to indicate that the failure should be reported
} FailureType;

void
set_failure_type (FailureType failure_type);

/**
* TypeLoadFailureCallback:
* @param klass: Class in which the failure was detected.
* @param fmt: printf-style error message string.
*
* The callback is responsible for processing the failure information provided by the @klass parameter and the error message format string @fmt.
* If a deferred failure occurs, the callback should return FALSE to let the AOT compiler proceed with the class layout setup.
* Otherwise, if the callback returns TRUE, it indicates that the failure should be reported.
*
* @returns: TRUE if the failure is handled and the runtime should not proceed with class setup, FALSE if the failure should be deferred for runtime class setup.
*
*/
typedef gboolean (*TypeLoadFailureCallback)(MonoClass *klass, const char * fmt, ...) MONO_ATTR_FORMAT_PRINTF(2,3);

extern TypeLoadFailureCallback type_load_failure_callback;

gboolean
mono_class_set_type_load_deferred_failure (MonoClass *klass, const char * fmt, ...);

gboolean
mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...);

#if HOST_WIN32
#if HOST_X86 || HOST_AMD64

Expand Down
72 changes: 72 additions & 0 deletions src/mono/mono/utils/mono-error.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@
va_end (args); \
} while (0)

TypeLoadFailureCallback type_load_failure_callback = mono_class_set_type_load_failure;

static void
mono_error_set_generic_errorv (MonoError *oerror, const char *name_space, const char *name, const char *msg_format, va_list args);

Expand Down Expand Up @@ -908,3 +910,73 @@ mono_error_set_first_argument (MonoError *oerror, const char *first_argument)
to->first_argument = g_strdup (first_argument);
to->flags |= MONO_ERROR_FREE_STRINGS;
}

/**
* mono_class_set_type_load_deferred_failure:
* \param klass class in which the failure was detected
* \param fmt \c printf -style error message string.
*
* Sets a deferred failure in the class and prints a warning message.
* The deferred failure allows the runtime to attempt setting up the class layout at runtime.
*
* LOCKING: Acquires the loader lock.
*
* \returns FALSE
*/
gboolean
mono_class_set_type_load_deferred_failure (MonoClass *klass, const char * fmt, ...)
{
if (!mono_class_has_deferred_failure (klass)) {
va_list args;

va_start (args, fmt);
g_warning ("Warning: %s", fmt, args);
va_end (args);

mono_class_set_deferred_failure (klass);
}

return FALSE;
}

/**
* mono_class_set_type_load_failure:
* \param klass class in which the failure was detected
* \param fmt \c printf -style error message string.
*
* Collect detected failure informaion in the class for later processing.
* The error is stored as a MonoErrorBoxed as with mono_error_set_type_load_class()
* Note that only the first failure is kept.
*
* LOCKING: Acquires the loader lock.
*
* \returns TRUE
*/
gboolean
mono_class_set_type_load_failure (MonoClass *klass, const char * fmt, ...)
{
if (!mono_class_has_failure (klass)) {
ERROR_DECL (prepare_error);
va_list args;

va_start (args, fmt);
mono_error_vset_type_load_class (prepare_error, klass, fmt, args);
va_end (args);

MonoErrorBoxed *box = mono_error_box (prepare_error, m_class_get_image (klass));
mono_error_cleanup (prepare_error);
mono_class_set_failure (klass, box);
}

return TRUE;
}

void set_failure_type(FailureType failure_type) {
if (failure_type == DEFERRED_FAILURE) {
type_load_failure_callback = mono_class_set_type_load_deferred_failure;
} else if (failure_type == IMMEDIATE_FAILURE) {
type_load_failure_callback = mono_class_set_type_load_failure;
} else {
g_assert_not_reached ();
}
}
3 changes: 3 additions & 0 deletions src/tests/issues.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2777,6 +2777,9 @@
<ExcludeList Include = "$(XunitTestBinBase)/Interop/NativeLibrary/API/NativeLibraryTests/**">
<Issue>Needs coreclr build</Issue>
</ExcludeList>
<ExcludeList Include = "$(XunitTestBinBase)/Loader/classloader/InlineArray/InlineArrayInvalid/**">
<Issue>https://github.com/dotnet/runtime/issues/86327</Issue>
</ExcludeList>
</ItemGroup>

<ItemGroup Condition="'$(RuntimeFlavor)' == 'mono' and ('$(RuntimeVariant)' == 'llvmfullaot' or '$(RuntimeVariant)' == 'llvmaot' or '$(TargetOS)' == 'ios') and '$(TargetArchitecture)' == 'arm64'">
Expand Down