#define HAVE_AEABI_READ_TP 1
#endif
+#define THUNK_SIZE (3 * 4)
+
#ifdef __native_client_codegen__
const guint kNaClAlignment = kNaClAlignmentARM;
const guint kNaClAlignmentMask = kNaClAlignmentMaskARM;
} else {
ARM_BL (code, 0);
}
+ cfg->thunk_area += THUNK_SIZE;
#endif
return code;
}
#endif /* #ifndef DISABLE_JIT */
-typedef struct {
- guchar *code;
- const guchar *target;
- int absolute;
- int found;
-} PatchData;
-
#define is_call_imm(diff) ((gint)(diff) >= -33554432 && (gint)(diff) <= 33554431)
-static int
-search_thunk_slot (void *data, int csize, int bsize, void *user_data) {
- PatchData *pdata = (PatchData*)user_data;
- guchar *code = data;
- guint32 *thunks = data;
- guint32 *endthunks = (guint32*)(code + bsize);
- int count = 0;
- int difflow, diffhigh;
-
- /* always ensure a call from pdata->code can reach to the thunks without further thunks */
- difflow = (char*)pdata->code - (char*)thunks;
- diffhigh = (char*)pdata->code - (char*)endthunks;
- if (!((is_call_imm (thunks) && is_call_imm (endthunks)) || (is_call_imm (difflow) && is_call_imm (diffhigh))))
- return 0;
+static void
+emit_thunk (guint8 *code, gconstpointer target)
+{
+ guint8 *p = code;
- /*
- * The thunk is composed of 3 words:
- * load constant from thunks [2] into ARM_IP
- * bx to ARM_IP
- * address constant
- * Note that the LR register is already setup
- */
- //g_print ("thunk nentries: %d\n", ((char*)endthunks - (char*)thunks)/16);
- if ((pdata->found == 2) || (pdata->code >= code && pdata->code <= code + csize)) {
- while (thunks < endthunks) {
- //g_print ("looking for target: %p at %p (%08x-%08x)\n", pdata->target, thunks, thunks [0], thunks [1]);
- if (thunks [2] == (guint32)pdata->target) {
- arm_patch (pdata->code, (guchar*)thunks);
- mono_arch_flush_icache (pdata->code, 4);
- pdata->found = 1;
- return 1;
- } else if ((thunks [0] == 0) && (thunks [1] == 0) && (thunks [2] == 0)) {
- /* found a free slot instead: emit thunk */
- /* ARMREG_IP is fine to use since this can't be an IMT call
- * which is indirect
- */
- code = (guchar*)thunks;
- ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
- if (thumb_supported)
- ARM_BX (code, ARMREG_IP);
- else
- ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
- thunks [2] = (guint32)pdata->target;
- mono_arch_flush_icache ((guchar*)thunks, 12);
-
- arm_patch (pdata->code, (guchar*)thunks);
- mono_arch_flush_icache (pdata->code, 4);
- pdata->found = 1;
- return 1;
- }
- /* skip 12 bytes, the size of the thunk */
- thunks += 3;
- count++;
- }
- //g_print ("failed thunk lookup for %p from %p at %p (%d entries)\n", pdata->target, pdata->code, data, count);
- }
- return 0;
+ ARM_LDR_IMM (code, ARMREG_IP, ARMREG_PC, 0);
+ if (thumb_supported)
+ ARM_BX (code, ARMREG_IP);
+ else
+ ARM_MOV_REG_REG (code, ARMREG_PC, ARMREG_IP);
+ *(guint32*)code = (guint32)target;
+ code += 4;
+ mono_arch_flush_icache (p, code - p);
}
static void
-handle_thunk (MonoDomain *domain, int absolute, guchar *code, const guchar *target, MonoCodeManager *dyn_code_mp)
+handle_thunk (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *target)
{
- PatchData pdata;
+ MonoJitInfo *ji = NULL;
+ MonoThunkJitInfo *info;
+ guint8 *thunks, *p;
+ int thunks_size;
+ guint8 *orig_target;
+ guint8 *target_thunk;
if (!domain)
domain = mono_domain_get ();
- pdata.code = code;
- pdata.target = target;
- pdata.absolute = absolute;
- pdata.found = 0;
+ if (cfg) {
+ /*
+ * This can be called multiple times during JITting,
+ * save the current position in cfg->arch to avoid
+ * doing a O(n^2) search.
+ */
+ if (!cfg->arch.thunks) {
+ cfg->arch.thunks = cfg->thunks;
+ cfg->arch.thunks_size = cfg->thunk_area;
+ }
+ thunks = cfg->arch.thunks;
+ thunks_size = cfg->arch.thunks_size;
+ if (!thunks_size) {
+ g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, mono_method_full_name (cfg->method, TRUE));
+ g_assert_not_reached ();
+ }
- if (dyn_code_mp) {
- mono_code_manager_foreach (dyn_code_mp, search_thunk_slot, &pdata);
- }
+ g_assert (*(guint32*)thunks == 0);
+ emit_thunk (thunks, target);
+ arm_patch (code, thunks);
- if (pdata.found != 1) {
- mono_domain_lock (domain);
- mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
+ cfg->arch.thunks += THUNK_SIZE;
+ cfg->arch.thunks_size -= THUNK_SIZE;
+ } else {
+ ji = mini_jit_info_table_find (domain, (char*)code, NULL);
+ g_assert (ji);
+ info = mono_jit_info_get_thunk_info (ji);
+ g_assert (info);
- if (!pdata.found) {
- /* this uses the first available slot */
- pdata.found = 2;
- mono_domain_code_foreach (domain, search_thunk_slot, &pdata);
- }
- mono_domain_unlock (domain);
- }
+ thunks = (guint8*)ji->code_start + info->thunks_offset;
+ thunks_size = info->thunks_size;
- if (pdata.found != 1) {
- GHashTable *hash;
- GHashTableIter iter;
- MonoJitDynamicMethodInfo *ji;
+ orig_target = mono_arch_get_call_target (code + 4);
- /*
- * This might be a dynamic method, search its code manager. We can only
- * use the dynamic method containing CODE, since the others might be freed later.
- */
- pdata.found = 0;
+ mono_mini_arch_lock ();
- mono_domain_lock (domain);
- hash = domain_jit_info (domain)->dynamic_code_hash;
- if (hash) {
- /* FIXME: Speed this up */
- g_hash_table_iter_init (&iter, hash);
- while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&ji)) {
- mono_code_manager_foreach (ji->code_mp, search_thunk_slot, &pdata);
- if (pdata.found == 1)
+ target_thunk = NULL;
+ if (orig_target >= thunks && orig_target < thunks + thunks_size) {
+ /* The call already points to a thunk, because of trampolines etc. */
+ target_thunk = orig_target;
+ } else {
+ for (p = thunks; p < thunks + thunks_size; p += THUNK_SIZE) {
+ if (((guint32*)p) [0] == 0) {
+ /* Free entry */
+ target_thunk = p;
break;
+ }
}
}
- mono_domain_unlock (domain);
+
+ //printf ("THUNK: %p %p %p\n", code, target, target_thunk);
+
+ if (!target_thunk) {
+ mono_mini_arch_unlock ();
+ g_print ("thunk failed %p->%p, thunk space=%d method %s", code, target, thunks_size, cfg ? mono_method_full_name (cfg->method, TRUE) : mono_method_full_name (jinfo_get_method (ji), TRUE));
+ g_assert_not_reached ();
+ }
+
+ emit_thunk (target_thunk, target);
+ arm_patch (code, target_thunk);
+ mono_arch_flush_icache (code, 4);
+
+ mono_mini_arch_unlock ();
}
- if (pdata.found != 1)
- g_print ("thunk failed for %p from %p\n", target, code);
- g_assert (pdata.found == 1);
}
static void
-arm_patch_general (MonoDomain *domain, guchar *code, const guchar *target, MonoCodeManager *dyn_code_mp)
+arm_patch_general (MonoCompile *cfg, MonoDomain *domain, guchar *code, const guchar *target)
{
guint32 *code32 = (void*)code;
guint32 ins = *code32;
}
}
- handle_thunk (domain, TRUE, code, target, dyn_code_mp);
+ handle_thunk (cfg, domain, code, target);
return;
}
void
arm_patch (guchar *code, const guchar *target)
{
- arm_patch_general (NULL, code, target, NULL);
+ arm_patch_general (NULL, NULL, code, target);
}
/*
case OP_CALL_HANDLER:
mono_add_patch_info (cfg, code - cfg->native_code, MONO_PATCH_INFO_BB, ins->inst_target_bb);
code = mono_arm_patchable_bl (code, ARMCOND_AL);
+ cfg->thunk_area += THUNK_SIZE;
mono_cfg_add_try_hole (cfg, ins->inst_eh_block, code, bb);
break;
case OP_GET_EX_OBJ:
} while (0)
void
-mono_arch_patch_code (MonoMethod *method, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, MonoCodeManager *dyn_code_mp, gboolean run_cctors)
+mono_arch_patch_code_new (MonoCompile *cfg, MonoDomain *domain, guint8 *code, MonoJumpInfo *ji, gpointer target)
{
- MonoJumpInfo *patch_info;
- gboolean compile_aot = !run_cctors;
+ unsigned char *ip = ji->ip.i + code;
- for (patch_info = ji; patch_info; patch_info = patch_info->next) {
- unsigned char *ip = patch_info->ip.i + code;
- const unsigned char *target;
+ if (ji->type == MONO_PATCH_INFO_SWITCH) {
+ }
- if (patch_info->type == MONO_PATCH_INFO_SWITCH && !compile_aot) {
+ switch (ji->type) {
+ case MONO_PATCH_INFO_SWITCH: {
#ifdef USE_JUMP_TABLES
- gpointer *jt = mono_jumptable_get_entry (ip);
+ gpointer *jt = mono_jumptable_get_entry (ip);
#else
- gpointer *jt = (gpointer*)(ip + 8);
+ gpointer *jt = (gpointer*)(ip + 8);
#endif
- int i;
- /* jt is the inlined jump table, 2 instructions after ip
- * In the normal case we store the absolute addresses,
- * otherwise the displacements.
- */
- for (i = 0; i < patch_info->data.table->table_size; i++)
- jt [i] = code + (int)patch_info->data.table->table [i];
- continue;
- }
-
- if (compile_aot) {
- switch (patch_info->type) {
- case MONO_PATCH_INFO_BB:
- case MONO_PATCH_INFO_LABEL:
- break;
- default:
- /* No need to patch these */
- continue;
- }
- }
-
- target = mono_resolve_patch_target (method, domain, code, patch_info, run_cctors);
-
- switch (patch_info->type) {
- case MONO_PATCH_INFO_IP:
- g_assert_not_reached ();
- patch_lis_ori (ip, ip);
- continue;
- case MONO_PATCH_INFO_METHOD_REL:
- g_assert_not_reached ();
- *((gpointer *)(ip)) = code + patch_info->data.offset;
- continue;
- case MONO_PATCH_INFO_METHODCONST:
- case MONO_PATCH_INFO_CLASS:
- case MONO_PATCH_INFO_IMAGE:
- case MONO_PATCH_INFO_FIELD:
- case MONO_PATCH_INFO_VTABLE:
- case MONO_PATCH_INFO_IID:
- case MONO_PATCH_INFO_SFLDA:
- case MONO_PATCH_INFO_LDSTR:
- case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
- case MONO_PATCH_INFO_LDTOKEN:
- g_assert_not_reached ();
- /* from OP_AOTCONST : lis + ori */
- patch_lis_ori (ip, target);
- continue;
- case MONO_PATCH_INFO_R4:
- case MONO_PATCH_INFO_R8:
- g_assert_not_reached ();
- *((gconstpointer *)(ip + 2)) = patch_info->data.target;
- continue;
- case MONO_PATCH_INFO_EXC_NAME:
- g_assert_not_reached ();
- *((gconstpointer *)(ip + 1)) = patch_info->data.name;
- continue;
- case MONO_PATCH_INFO_NONE:
- case MONO_PATCH_INFO_BB_OVF:
- case MONO_PATCH_INFO_EXC_OVF:
- /* everything is dealt with at epilog output time */
- continue;
- default:
- break;
- }
- arm_patch_general (domain, ip, target, dyn_code_mp);
+ int i;
+ /* jt is the inlined jump table, 2 instructions after ip
+ * In the normal case we store the absolute addresses,
+ * otherwise the displacements.
+ */
+ for (i = 0; i < ji->data.table->table_size; i++)
+ jt [i] = code + (int)ji->data.table->table [i];
+ break;
+ }
+ case MONO_PATCH_INFO_IP:
+ g_assert_not_reached ();
+ patch_lis_ori (ip, ip);
+ break;
+ case MONO_PATCH_INFO_METHOD_REL:
+ g_assert_not_reached ();
+ *((gpointer *)(ip)) = target;
+ break;
+ case MONO_PATCH_INFO_METHODCONST:
+ case MONO_PATCH_INFO_CLASS:
+ case MONO_PATCH_INFO_IMAGE:
+ case MONO_PATCH_INFO_FIELD:
+ case MONO_PATCH_INFO_VTABLE:
+ case MONO_PATCH_INFO_IID:
+ case MONO_PATCH_INFO_SFLDA:
+ case MONO_PATCH_INFO_LDSTR:
+ case MONO_PATCH_INFO_TYPE_FROM_HANDLE:
+ case MONO_PATCH_INFO_LDTOKEN:
+ g_assert_not_reached ();
+ /* from OP_AOTCONST : lis + ori */
+ patch_lis_ori (ip, target);
+ break;
+ case MONO_PATCH_INFO_R4:
+ case MONO_PATCH_INFO_R8:
+ g_assert_not_reached ();
+ *((gconstpointer *)(ip + 2)) = target;
+ break;
+ case MONO_PATCH_INFO_EXC_NAME:
+ g_assert_not_reached ();
+ *((gconstpointer *)(ip + 1)) = target;
+ break;
+ case MONO_PATCH_INFO_NONE:
+ case MONO_PATCH_INFO_BB_OVF:
+ case MONO_PATCH_INFO_EXC_OVF:
+ /* everything is dealt with at epilog output time */
+ break;
+ default:
+ arm_patch_general (cfg, domain, ip, target);
+ break;
}
}
patch_info->data.name = "mono_arch_throw_corlib_exception";
patch_info->ip.i = code - cfg->native_code;
ARM_BL (code, 0);
+ cfg->thunk_area += THUNK_SIZE;
*(guint32*)(gpointer)code = exc_class->type_token;
code += 4;
#endif