* [aot] Emit class references in EH info in such as way that they can be skipped when reading them in async context.
* [aot] Rewrite the LLVM EH info loading code by moving allocations to callers so the lower level code is async safe.
* [aot] Make the memory allocation in the loading of llvm EH info async safe. Remove fields from MonoLLVMFDEInfo which are passed as parameters.
* [runtime] Add the ability to store the unwind info address/length in the MonoJitInfo structure instead of referencing it using the 'unwind_info' field.
* [aot] Make the loading of llvm EH information async safe.
int thunks_size;
} MonoThunkJitInfo;
+typedef struct {
+ guint8 *unw_info;
+ int unw_info_len;
+} MonoUnwindJitInfo;
+
typedef enum {
JIT_INFO_NONE = 0,
JIT_INFO_HAS_GENERIC_JIT_INFO = (1 << 0),
JIT_INFO_HAS_TRY_BLOCK_HOLES = (1 << 1),
JIT_INFO_HAS_ARCH_EH_INFO = (1 << 2),
- JIT_INFO_HAS_THUNK_INFO = (1 << 3)
+ JIT_INFO_HAS_THUNK_INFO = (1 << 3),
+ /*
+ * If this is set, the unwind info is stored in the structure, instead of being pointed to by the
+ * 'unwind_info' field.
+ */
+ JIT_INFO_HAS_UNWIND_INFO = (1 << 4)
} MonoJitInfoFlags;
struct _MonoJitInfo {
gboolean has_try_block_holes:1;
gboolean has_arch_eh_info:1;
gboolean has_thunk_info:1;
+ gboolean has_unwind_info:1;
gboolean from_aot:1;
gboolean from_llvm:1;
gboolean dbg_attrs_inited:1;
MonoThunkJitInfo*
mono_jit_info_get_thunk_info (MonoJitInfo *ji);
+MonoUnwindJitInfo*
+mono_jit_info_get_unwind_info (MonoJitInfo *ji);
+
/*
* Installs a new function which is used to return a MonoJitInfo for a method inside
* an AOT module.
size += sizeof (MonoArchEHJitInfo);
if (flags & JIT_INFO_HAS_THUNK_INFO)
size += sizeof (MonoThunkJitInfo);
+ if (flags & JIT_INFO_HAS_UNWIND_INFO)
+ size += sizeof (MonoUnwindJitInfo);
return size;
}
ji->has_arch_eh_info = 1;
if (flags & JIT_INFO_HAS_THUNK_INFO)
ji->has_thunk_info = 1;
+ if (flags & JIT_INFO_HAS_UNWIND_INFO)
+ ji->has_unwind_info = 1;
}
/**
return NULL;
}
}
+
+MonoUnwindJitInfo*
+mono_jit_info_get_unwind_info (MonoJitInfo *ji)
+{
+ if (ji->has_unwind_info) {
+ char *ptr = (char*)&ji->clauses [ji->num_clauses];
+ if (ji->has_generic_jit_info)
+ ptr += sizeof (MonoGenericJitInfo);
+ if (ji->has_try_block_holes)
+ ptr += try_block_hole_table_size (ji);
+ if (ji->has_arch_eh_info)
+ ptr += sizeof (MonoArchEHJitInfo);
+ if (ji->has_thunk_info)
+ ptr += sizeof (MonoThunkJitInfo);
+ return (MonoUnwindJitInfo*)ptr;
+ } else {
+ return NULL;
+ }
+}
clause = &header->clauses [k];
encode_value (clause->flags, p, &p);
- if (clause->data.catch_class) {
- encode_value (1, p, &p);
- encode_klass_ref (acfg, clause->data.catch_class, p, &p);
- } else {
- encode_value (0, p, &p);
+ if (!(clause->flags == MONO_EXCEPTION_CLAUSE_FILTER || clause->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) {
+ if (clause->data.catch_class) {
+ guint8 *buf2, *p2;
+ int len;
+
+ buf2 = (guint8 *)g_malloc (4096);
+ p2 = buf2;
+ encode_klass_ref (acfg, clause->data.catch_class, p2, &p2);
+ len = p2 - buf2;
+ g_assert (len < 4096);
+ encode_value (len, p, &p);
+ memcpy (p, buf2, len);
+ p += p2 - buf2;
+ g_free (buf2);
+ } else {
+ encode_value (0, p, &p);
+ }
}
/* Emit the IL ranges too, since they might not be available at runtime */
*
* Decode the EH information emitted by our modified LLVM compiler and construct a
* MonoJitInfo structure from it.
- * LOCKING: Acquires the domain lock.
+ * If JINFO is NULL, set OUT_LLVM_CLAUSES to the number of llvm level clauses.
+ * This function is async safe when called in async context.
*/
-static MonoJitInfo*
-decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain,
- MonoMethod *method, guint8 *code, guint32 code_len,
+static void
+decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain, MonoJitInfo *jinfo,
+ guint8 *code, guint32 code_len,
MonoJitExceptionInfo *clauses, int num_clauses,
- MonoJitInfoFlags flags,
GSList **nesting,
- int *this_reg, int *this_offset)
+ int *this_reg, int *this_offset, int *out_llvm_clauses)
{
guint8 *p, *code1, *code2;
guint8 *fde, *cie, *code_start, *code_end;
MonoJitExceptionInfo *ei;
guint32 fde_len, ei_len, nested_len, nindex;
gpointer *type_info;
- MonoJitInfo *jinfo;
MonoLLVMFDEInfo info;
+ guint8 *unw_info;
+ gboolean async;
+
+ async = mono_thread_info_is_async_context ();
if (!amodule->mono_eh_frame) {
- jinfo = (MonoJitInfo *)mono_domain_alloc0_lock_free (domain, mono_jit_info_size (flags, num_clauses, 0));
- mono_jit_info_init (jinfo, method, code, code_len, flags, num_clauses, 0);
+ if (!jinfo) {
+ *out_llvm_clauses = num_clauses;
+ return;
+ }
memcpy (jinfo->clauses, clauses, num_clauses * sizeof (MonoJitExceptionInfo));
- return jinfo;
+ return;
}
g_assert (amodule->mono_eh_frame && code);
/* This won't overflow because there is +1 entry in the table */
fde_len = table [(pos * 2) + 2 + 1] - table [(pos * 2) + 1];
- mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, code_start, &info);
- ei = info.ex_info;
+ /* Compute lengths */
+ mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, code_start, &info, NULL, NULL, NULL);
+
+ if (async) {
+ /* These are leaked, but the leak is bounded */
+ ei = mono_domain_alloc0_lock_free (domain, info.ex_info_len * sizeof (MonoJitExceptionInfo));
+ type_info = mono_domain_alloc0_lock_free (domain, info.ex_info_len * sizeof (gpointer));
+ unw_info = mono_domain_alloc0_lock_free (domain, info.unw_info_len);
+ } else {
+ ei = (MonoJitExceptionInfo *)g_malloc0 (info.ex_info_len * sizeof (MonoJitExceptionInfo));
+ type_info = (gpointer *)g_malloc0 (info.ex_info_len * sizeof (gpointer));
+ unw_info = (guint8*)g_malloc0 (info.unw_info_len);
+ }
+ mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, code_start, &info, ei, type_info, unw_info);
+
ei_len = info.ex_info_len;
- type_info = info.type_info;
*this_reg = info.this_reg;
*this_offset = info.this_offset;
+ /*
+ * LLVM might represent one IL region with multiple regions.
+ */
+
/* Count number of nested clauses */
nested_len = 0;
for (i = 0; i < ei_len; ++i) {
nested_len ++;
}
- /*
- * LLVM might represent one IL region with multiple regions, so have to
- * allocate a new JI.
- */
- jinfo =
- (MonoJitInfo *)mono_domain_alloc0_lock_free (domain, mono_jit_info_size (flags, ei_len + nested_len, 0));
- mono_jit_info_init (jinfo, method, code, code_len, flags, ei_len + nested_len, 0);
+ if (!jinfo) {
+ *out_llvm_clauses = ei_len + nested_len;
+ return;
+ }
- jinfo->unwind_info = mono_cache_unwind_info (info.unw_info, info.unw_info_len);
- /* This signals that unwind_info points to a normal cached unwind info */
- jinfo->from_aot = 0;
- jinfo->from_llvm = 1;
+ /* Store the unwind info addr/length in the MonoJitInfo structure itself so its async safe */
+ MonoUnwindJitInfo *jinfo_unwind = mono_jit_info_get_unwind_info (jinfo);
+ g_assert (jinfo_unwind);
+ jinfo_unwind->unw_info = unw_info;
+ jinfo_unwind->unw_info_len = info.unw_info_len;
for (i = 0; i < ei_len; ++i) {
/*
}
}
g_assert (nindex == ei_len + nested_len);
-
- return jinfo;
}
static gpointer
MonoJitExceptionInfo *clauses;
GSList **nesting;
- // FIXME: async
- g_assert (!async);
-
/*
* Part of the info is encoded by the AOT compiler, the rest is in the .eh_frame
* section.
*/
- clauses = g_new0 (MonoJitExceptionInfo, num_clauses);
- nesting = g_new0 (GSList*, num_clauses);
+ if (async) {
+ if (num_clauses < 16) {
+ clauses = g_newa (MonoJitExceptionInfo, num_clauses);
+ nesting = g_newa (GSList*, num_clauses);
+ } else {
+ clauses = alloc0_jit_info_data (domain, sizeof (MonoJitExceptionInfo) * num_clauses, TRUE);
+ nesting = alloc0_jit_info_data (domain, sizeof (GSList*) * num_clauses, TRUE);
+ }
+ memset (clauses, 0, sizeof (MonoJitExceptionInfo) * num_clauses);
+ memset (nesting, 0, sizeof (GSList*) * num_clauses);
+ } else {
+ clauses = g_new0 (MonoJitExceptionInfo, num_clauses);
+ nesting = g_new0 (GSList*, num_clauses);
+ }
for (i = 0; i < num_clauses; ++i) {
MonoJitExceptionInfo *ei = &clauses [i];
ei->flags = decode_value (p, &p);
- if (decode_value (p, &p)) {
- ei->data.catch_class = decode_klass_ref (amodule, p, &p, &error);
- mono_error_cleanup (&error); /* FIXME don't swallow the error */
+ if (!(ei->flags == MONO_EXCEPTION_CLAUSE_FILTER || ei->flags == MONO_EXCEPTION_CLAUSE_FINALLY)) {
+ int len = decode_value (p, &p);
+
+ if (len > 0) {
+ if (async) {
+ p += len;
+ } else {
+ ei->data.catch_class = decode_klass_ref (amodule, p, &p, &error);
+ mono_error_cleanup (&error); /* FIXME don't swallow the error */
+ }
+ }
}
ei->clause_index = i;
int nesting_index = decode_value (p, &p);
if (nesting_index == -1)
break;
+ // FIXME: async
+ g_assert (!async);
nesting [i] = g_slist_prepend (nesting [i], GINT_TO_POINTER (nesting_index));
}
}
- jinfo = decode_llvm_mono_eh_frame (amodule, domain, method, code, code_len, clauses, num_clauses, flags, nesting, &this_reg, &this_offset);
+ flags |= JIT_INFO_HAS_UNWIND_INFO;
+
+ int num_llvm_clauses;
+ /* Get the length first */
+ decode_llvm_mono_eh_frame (amodule, domain, NULL, code, code_len, clauses, num_clauses, nesting, &this_reg, &this_offset, &num_llvm_clauses);
+ len = mono_jit_info_size (flags, num_llvm_clauses, num_holes);
+ jinfo = (MonoJitInfo *)alloc0_jit_info_data (domain, len, async);
+ mono_jit_info_init (jinfo, method, code, code_len, flags, num_llvm_clauses, num_holes);
- g_free (clauses);
- for (i = 0; i < num_clauses; ++i)
- g_slist_free (nesting [i]);
- g_free (nesting);
+ decode_llvm_mono_eh_frame (amodule, domain, jinfo, code, code_len, clauses, num_clauses, nesting, &this_reg, &this_offset, NULL);
+
+ if (!async) {
+ g_free (clauses);
+ for (i = 0; i < num_clauses; ++i)
+ g_slist_free (nesting [i]);
+ g_free (nesting);
+ }
} else {
len = mono_jit_info_size (flags, num_clauses, num_holes);
jinfo = (MonoJitInfo *)alloc0_jit_info_data (domain, len, async);
guint8*
mono_jinfo_get_unwind_info (MonoJitInfo *ji, guint32 *unwind_info_len)
{
- if (ji->from_aot)
+ if (ji->has_unwind_info) {
+ /* The address/length in the MonoJitInfo structure itself */
+ MonoUnwindJitInfo *info = mono_jit_info_get_unwind_info (ji);
+ *unwind_info_len = info->unw_info_len;
+ return info->unw_info;
+ } else if (ji->from_aot)
return mono_aot_get_unwind_info (ji, unwind_info_len);
else
return mono_get_cached_unwind_info (ji->unwind_info, unwind_info_len);
guint32 ei_len, i, nested_len;
gpointer *type_info;
gint32 *table;
+ guint8 *unw_info;
/*
* Decode the one element EH table emitted by the MonoException class
fde = (guint8*)eh_frame + fde_offset;
cie = (guint8*)table;
- mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, cfg->native_code, &info);
+ /* Compute lengths */
+ mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, cfg->native_code, &info, NULL, NULL, NULL);
- cfg->encoded_unwind_ops = info.unw_info;
+ ei = (MonoJitExceptionInfo *)g_malloc0 (info.ex_info_len * sizeof (MonoJitExceptionInfo));
+ type_info = (gpointer *)g_malloc0 (info.ex_info_len * sizeof (gpointer));
+ unw_info = (guint8*)g_malloc0 (info.unw_info_len);
+
+ mono_unwind_decode_llvm_mono_fde (fde, fde_len, cie, cfg->native_code, &info, ei, type_info, unw_info);
+
+ cfg->encoded_unwind_ops = unw_info;
cfg->encoded_unwind_ops_len = info.unw_info_len;
if (cfg->verbose_level > 1)
mono_print_unwind_info (cfg->encoded_unwind_ops, cfg->encoded_unwind_ops_len);
cfg->llvm_this_offset = info.this_offset;
}
- ei = info.ex_info;
ei_len = info.ex_info_len;
- type_info = info.type_info;
// Nested clauses are currently disabled
nested_len = 0;
/* Data retrieved from an LLVM Mono FDE entry */
typedef struct {
- /* Malloc'ed */
- guint8 *unw_info;
guint32 unw_info_len;
- MonoJitExceptionInfo *ex_info;
guint32 ex_info_len;
- gpointer *type_info;
+ int type_info_len;
int this_reg;
int this_offset;
} MonoLLVMFDEInfo;
void
-mono_unwind_decode_llvm_mono_fde (guint8 *fde, int fde_len, guint8 *cie, guint8 *code, MonoLLVMFDEInfo *res) MONO_LLVM_INTERNAL;
+mono_unwind_decode_llvm_mono_fde (guint8 *fde, int fde_len, guint8 *cie, guint8 *code, MonoLLVMFDEInfo *res, MonoJitExceptionInfo *ei, gpointer *type_info, guint8 *unw_info) MONO_LLVM_INTERNAL;
GSList* mono_unwind_get_cie_program (void);
#endif
/* Version number of the AOT file format */
-#define MONO_AOT_FILE_VERSION 139
+#define MONO_AOT_FILE_VERSION 140
//TODO: This is x86/amd64 specific.
#define mono_simd_shuffle_mask(a,b,c,d) ((a) | ((b) << 2) | ((c) << 4) | ((d) << 6))
* decode_lsda:
*
* Decode the Mono specific Language Specific Data Area generated by LLVM.
+ * This function is async safe.
*/
static void
-decode_lsda (guint8 *lsda, guint8 *code, MonoJitExceptionInfo **ex_info, guint32 *ex_info_len, gpointer **type_info, int *this_reg, int *this_offset)
+decode_lsda (guint8 *lsda, guint8 *code, MonoJitExceptionInfo *ex_info, gpointer *type_info, guint32 *ex_info_len, int *this_reg, int *this_offset)
{
guint8 *p;
int i, ncall_sites, this_encoding;
ncall_sites = decode_uleb128 (p, &p);
p = (guint8*)ALIGN_TO ((mgreg_t)p, 4);
- if (ex_info) {
- *ex_info = (MonoJitExceptionInfo *)g_malloc0 (ncall_sites * sizeof (MonoJitExceptionInfo));
+ if (ex_info_len)
*ex_info_len = ncall_sites;
- }
- if (type_info)
- *type_info = (gpointer *)g_malloc0 (ncall_sites * sizeof (gpointer));
for (i = 0; i < ncall_sites; ++i) {
int block_start_offset, block_size, landing_pad;
//printf ("X: %p %d\n", landing_pad, *(int*)tinfo);
if (ex_info) {
- if (*type_info)
- (*type_info) [i] = tinfo;
- (*ex_info)[i].try_start = code + block_start_offset;
- (*ex_info)[i].try_end = code + block_start_offset + block_size;
- (*ex_info)[i].handler_start = code + landing_pad;
+ if (type_info)
+ type_info [i] = tinfo;
+ ex_info[i].try_start = code + block_start_offset;
+ ex_info[i].try_end = code + block_start_offset + block_size;
+ ex_info[i].handler_start = code + landing_pad;
}
}
}
if (lsda_offset != 0) {
lsda = fde_aug + lsda_offset;
- decode_lsda (lsda, code, ex_info, ex_info_len, type_info, this_reg, this_offset);
+ /* Get the lengths first */
+ guint32 len;
+ decode_lsda (lsda, code, NULL, NULL, &len, this_reg, this_offset);
+
+ if (ex_info)
+ *ex_info = (MonoJitExceptionInfo *)g_malloc0 (len * sizeof (MonoJitExceptionInfo));
+ if (type_info)
+ *type_info = (gpointer *)g_malloc0 (len * sizeof (gpointer));
+
+ decode_lsda (lsda, code, ex_info ? *ex_info : NULL, type_info ? *type_info : NULL, ex_info_len, this_reg, this_offset);
}
}
* mono_unwind_decode_mono_fde:
*
* Decode an FDE entry in the LLVM emitted mono EH frame.
- * info->ex_info is set to a malloc-ed array of MonoJitExceptionInfo structures,
- * only try_start, try_end and handler_start is set.
- * info->type_info is set to a malloc-ed array containing the ttype table from the
- * LSDA.
+ * If EI/TYPE_INFO/UNW_INFO are NULL, compute only the value of the scalar fields in INFO.
+ * Otherwise:
+ * - Fill out EX_INFO with try_start, try_end and handler_start.
+ * - Fill out TYPE_INFO with the ttype table from the LSDA.
+ * - Fill out UNW_INFO with the unwind info.
+ * This function is async safe.
*/
void
-mono_unwind_decode_llvm_mono_fde (guint8 *fde, int fde_len, guint8 *cie, guint8 *code, MonoLLVMFDEInfo *res)
+mono_unwind_decode_llvm_mono_fde (guint8 *fde, int fde_len, guint8 *cie, guint8 *code, MonoLLVMFDEInfo *res, MonoJitExceptionInfo *ex_info, gpointer *type_info, guint8 *unw_info)
{
guint8 *p, *fde_aug, *cie_cfi, *fde_cfi, *buf;
int has_aug, aug_len, cie_cfi_len, fde_cfi_len;
/* The LSDA is embedded directly into the FDE */
lsda = fde_aug;
- decode_lsda (lsda, code, &res->ex_info, &res->ex_info_len, &res->type_info, &res->this_reg, &res->this_offset);
+ /* Get the lengths first */
+ decode_lsda (lsda, code, NULL, NULL, &res->ex_info_len, &res->this_reg, &res->this_offset);
+
+ decode_lsda (lsda, code, ex_info, type_info, NULL, &res->this_reg, &res->this_offset);
}
/* Decode CIE */
cie_cfi_len = p - cie_cfi;
fde_cfi_len = (fde + fde_len - fde_cfi);
- buf = (guint8 *)g_malloc0 (cie_cfi_len + fde_cfi_len);
- memcpy (buf, cie_cfi, cie_cfi_len);
- memcpy (buf + cie_cfi_len, fde_cfi, fde_cfi_len);
+ buf = unw_info;
+ if (buf) {
+ memcpy (buf, cie_cfi, cie_cfi_len);
+ memcpy (buf + cie_cfi_len, fde_cfi, fde_cfi_len);
+ }
res->unw_info_len = cie_cfi_len + fde_cfi_len;
- res->unw_info = buf;
}
/*