2008-08-19 Zoltan Varga <vargaz@gmail.com>
[mono.git] / mono / mini / mini.c
index c06f97187a9e07718d440e45fe0f2029ed3cbea8..9574bc14f9642ed3d793fcaed122095305bf9155 100644 (file)
@@ -195,8 +195,6 @@ static int mini_verbose = 0;
 #define mono_jit_unlock() LeaveCriticalSection (&jit_mutex)
 static CRITICAL_SECTION jit_mutex;
 
-static GHashTable *rgctx_lazy_fetch_trampoline_hash = NULL;
-
 static MonoCodeManager *global_codeman = NULL;
 
 /* FIXME: Make this static again */
@@ -950,7 +948,7 @@ mono_unlink_bblock (MonoCompile *cfg, MonoBasicBlock *from, MonoBasicBlock* to)
  *
  *   Return whenever BB1 and BB2 are linked in the CFG.
  */
-static gboolean
+gboolean
 mono_bblocks_linked (MonoBasicBlock *bb1, MonoBasicBlock *bb2)
 {
        int i;
@@ -1168,8 +1166,8 @@ split_bblock (MonoCompile *cfg, MonoBasicBlock *first, MonoBasicBlock *second) {
        }
 }
 
-static guint32
-reverse_branch_op (guint32 opcode)
+guint32
+mono_reverse_branch_op (guint32 opcode)
 {
        static const int reverse_map [] = {
                CEE_BNE_UN, CEE_BLT, CEE_BLE, CEE_BGT, CEE_BGE,
@@ -1928,6 +1926,8 @@ mono_op_imm_to_op (int opcode)
                return OP_ISUB;
        case OP_LSUB_IMM:
                return OP_LSUB;
+       case OP_IMUL_IMM:
+               return OP_IMUL;
        case OP_AND_IMM:
 #if SIZEOF_VOID_P == 4
                return OP_IAND;
@@ -4924,48 +4924,12 @@ get_runtime_generic_context (MonoCompile *cfg, MonoMethod *method, int context_u
        }
 }
 
-gpointer
-mini_create_rgctx_lazy_fetch_trampoline (guint32 offset)
-{
-       static gboolean inited = FALSE;
-       static int num_trampolines = 0;
-
-       gpointer tramp, ptr;
-
-       mono_jit_lock ();
-       if (rgctx_lazy_fetch_trampoline_hash)
-               tramp = g_hash_table_lookup (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset));
-       else
-               tramp = NULL;
-       mono_jit_unlock ();
-       if (tramp)
-               return tramp;
-
-       tramp = mono_arch_create_rgctx_lazy_fetch_trampoline (offset);
-       ptr = mono_create_ftnptr (mono_get_root_domain (), tramp);
-
-       mono_jit_lock ();
-       if (!rgctx_lazy_fetch_trampoline_hash)
-               rgctx_lazy_fetch_trampoline_hash = g_hash_table_new (NULL, NULL);
-       g_hash_table_insert (rgctx_lazy_fetch_trampoline_hash, GUINT_TO_POINTER (offset), ptr);
-       mono_jit_unlock ();
-
-       if (!inited) {
-               mono_counters_register ("RGCTX num lazy fetch trampolines",
-                               MONO_COUNTER_GENERICS | MONO_COUNTER_INT, &num_trampolines);
-               inited = TRUE;
-       }
-       num_trampolines++;
-
-       return ptr;
-}
-
 static MonoInst*
 get_runtime_generic_context_other_table_ptr (MonoCompile *cfg, MonoBasicBlock *bblock,
        MonoInst *rgc_ptr, guint32 slot, const unsigned char *ip)
 {
        MonoMethodSignature *sig = helper_sig_rgctx_lazy_fetch_trampoline;
-       guint8 *tramp = mini_create_rgctx_lazy_fetch_trampoline (slot);
+       guint8 *tramp = mono_create_rgctx_lazy_fetch_trampoline (slot);
        int temp;
        MonoInst *field;
 
@@ -10991,8 +10955,8 @@ mono_destroy_compile (MonoCompile *cfg)
        mono_mempool_destroy (cfg->mempool);
        g_list_free (cfg->ldstr_list);
        g_hash_table_destroy (cfg->token_info_hash);
-
-       g_free (cfg->reverse_inst_list);
+       if (cfg->abs_patches)
+               g_hash_table_destroy (cfg->abs_patches);
 
        g_free (cfg->varinfo);
        g_free (cfg->vars);
@@ -11306,6 +11270,11 @@ mono_patch_info_dup_mp (MonoMemPool *mp, MonoJumpInfo *patch_info)
                res->data.table = mono_mempool_alloc (mp, sizeof (MonoJumpInfoBBTable));
                memcpy (res->data.table, patch_info->data.table, sizeof (MonoJumpInfoBBTable));
                break;
+       case MONO_PATCH_INFO_RGCTX_FETCH:
+               res->data.rgctx_entry = mono_mempool_alloc (mp, sizeof (MonoJumpInfoRgctxEntry));
+               memcpy (res->data.rgctx_entry, patch_info->data.rgctx_entry, sizeof (MonoJumpInfoRgctxEntry));
+               res->data.rgctx_entry->data = mono_patch_info_dup_mp (mp, res->data.rgctx_entry->data);
+               break;
        default:
                break;
        }
@@ -11329,16 +11298,17 @@ mono_patch_info_hash (gconstpointer data)
        case MONO_PATCH_INFO_CLASS:
        case MONO_PATCH_INFO_IID:
        case MONO_PATCH_INFO_ADJUSTED_IID:
-               return (ji->type << 8) | (gssize)ji->data.klass;
-       case MONO_PATCH_INFO_FIELD:
-       case MONO_PATCH_INFO_SFLDA:
-               return (ji->type << 8) | (gssize)ji->data.field;
+       case MONO_PATCH_INFO_CLASS_INIT:
        case MONO_PATCH_INFO_METHODCONST:
        case MONO_PATCH_INFO_METHOD:
        case MONO_PATCH_INFO_METHOD_JUMP:
-               return (ji->type << 8) | (gssize)ji->data.method;
        case MONO_PATCH_INFO_IMAGE:
-               return (ji->type << 8) | (gssize)ji->data.image;                
+       case MONO_PATCH_INFO_INTERNAL_METHOD:
+       case MONO_PATCH_INFO_JIT_ICALL_ADDR:
+       case MONO_PATCH_INFO_WRAPPER:
+       case MONO_PATCH_INFO_FIELD:
+       case MONO_PATCH_INFO_SFLDA:
+               return (ji->type << 8) | (gssize)ji->data.target;
        default:
                return (ji->type << 8);
        }
@@ -11371,7 +11341,7 @@ mono_patch_info_equal (gconstpointer ka, gconstpointer kb)
                        return 0;
                break;
        default:
-               if (ji1->data.name != ji2->data.name)
+               if (ji1->data.target != ji2->data.target)
                        return 0;
                break;
        }
@@ -11557,6 +11527,31 @@ mono_resolve_patch_target (MonoMethod *method, MonoDomain *domain, guint8 *code,
        case MONO_PATCH_INFO_GOT_OFFSET:
        case MONO_PATCH_INFO_NONE:
                break;
+       case MONO_PATCH_INFO_RGCTX_FETCH: {
+               MonoJumpInfoRgctxEntry *entry = patch_info->data.rgctx_entry;
+               guint32 slot = -1;
+
+               switch (entry->data->type) {
+               case MONO_PATCH_INFO_CLASS:
+                       slot = mono_method_lookup_or_register_other_info (entry->method, entry->in_mrgctx, &entry->data->data.klass->byval_arg, entry->info_type, mono_method_get_context (entry->method));
+                       break;
+               case MONO_PATCH_INFO_METHODCONST:
+                       slot = mono_method_lookup_or_register_other_info (entry->method, entry->in_mrgctx, entry->data->data.method, entry->info_type, mono_method_get_context (entry->method));
+                       break;
+               case MONO_PATCH_INFO_FIELD:
+                       slot = mono_method_lookup_or_register_other_info (entry->method, entry->in_mrgctx, entry->data->data.field, entry->info_type, mono_method_get_context (entry->method));
+                       break;
+               default:
+                       g_assert_not_reached ();
+                       break;
+               }
+
+               target = mono_create_rgctx_lazy_fetch_trampoline (slot);
+               break;
+       }
+       case MONO_PATCH_INFO_GENERIC_CLASS_INIT:
+               target = mono_get_trampoline_code (MONO_TRAMPOLINE_GENERIC_CLASS_INIT);
+               break;
        default:
                g_assert_not_reached ();
        }
@@ -11638,672 +11633,6 @@ decompose_pass (MonoCompile *cfg) {
        }
 }
 
-static void
-nullify_basic_block (MonoBasicBlock *bb) 
-{
-       bb->in_count = 0;
-       bb->out_count = 0;
-       bb->in_bb = NULL;
-       bb->out_bb = NULL;
-       bb->next_bb = NULL;
-       bb->code = bb->last_ins = NULL;
-       bb->cil_code = NULL;
-}
-
-static void 
-replace_out_block (MonoBasicBlock *bb, MonoBasicBlock *orig,  MonoBasicBlock *repl)
-{
-       int i;
-
-       for (i = 0; i < bb->out_count; i++) {
-               MonoBasicBlock *ob = bb->out_bb [i];
-               if (ob == orig) {
-                       if (!repl) {
-                               if (bb->out_count > 1) {
-                                       bb->out_bb [i] = bb->out_bb [bb->out_count - 1];
-                               }
-                               bb->out_count--;
-                       } else {
-                               bb->out_bb [i] = repl;
-                       }
-               }
-       }
-}
-
-static void 
-replace_in_block (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl)
-{
-       int i;
-
-       for (i = 0; i < bb->in_count; i++) {
-               MonoBasicBlock *ib = bb->in_bb [i];
-               if (ib == orig) {
-                       if (!repl) {
-                               if (bb->in_count > 1) {
-                                       bb->in_bb [i] = bb->in_bb [bb->in_count - 1];
-                               }
-                               bb->in_count--;
-                       } else {
-                               bb->in_bb [i] = repl;
-                       }
-               }
-       }
-}
-
-static void
-replace_out_block_in_code (MonoBasicBlock *bb, MonoBasicBlock *orig, MonoBasicBlock *repl) {
-       MonoInst *ins;
-       
-       for (ins = bb->code; ins != NULL; ins = ins->next) {
-               switch (ins->opcode) {
-               case OP_BR:
-                       if (ins->inst_target_bb == orig)
-                               ins->inst_target_bb = repl;
-                       break;
-               case OP_CALL_HANDLER:
-                       if (ins->inst_target_bb == orig)
-                               ins->inst_target_bb = repl;
-                       break;
-               case OP_SWITCH: {
-                       int i;
-                       int n = GPOINTER_TO_INT (ins->klass);
-                       for (i = 0; i < n; i++ ) {
-                               if (ins->inst_many_bb [i] == orig)
-                                       ins->inst_many_bb [i] = repl;
-                       }
-                       break;
-               }
-               default:
-                       if (MONO_IS_COND_BRANCH_OP (ins)) {
-                               if (ins->inst_true_bb == orig)
-                                       ins->inst_true_bb = repl;
-                               if (ins->inst_false_bb == orig)
-                                       ins->inst_false_bb = repl;
-                       } else if (MONO_IS_JUMP_TABLE (ins)) {
-                               int i;
-                               MonoJumpInfoBBTable *table = MONO_JUMP_TABLE_FROM_INS (ins);
-                               for (i = 0; i < table->table_size; i++ ) {
-                                       if (table->table [i] == orig)
-                                               table->table [i] = repl;
-                               }
-                       }
-
-                       break;
-               }
-       }
-}
-
-/**
-  * Check if a bb is useless (is just made of NOPs and ends with an
-  * unconditional branch, or nothing).
-  * If it is so, unlink it from the CFG and nullify it, and return TRUE.
-  * Otherwise, return FALSE;
-  */
-static gboolean
-remove_block_if_useless (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *previous_bb) {
-       MonoBasicBlock *target_bb = NULL;
-       MonoInst *inst;
-
-       /* Do not touch handlers */
-       if (bb->region != -1) {
-               bb->not_useless = TRUE;
-               return FALSE;
-       }
-       
-       MONO_BB_FOR_EACH_INS (bb, inst) {
-               switch (inst->opcode) {
-               case OP_NOP:
-                       break;
-               case OP_BR:
-                       target_bb = inst->inst_target_bb;
-                       break;
-               default:
-                       bb->not_useless = TRUE;
-                       return FALSE;
-               }
-       }
-       
-       if (target_bb == NULL) {
-               if ((bb->out_count == 1) && (bb->out_bb [0] == bb->next_bb)) {
-                       target_bb = bb->next_bb;
-               } else {
-                       /* Do not touch empty BBs that do not "fall through" to their next BB (like the exit BB) */
-                       return FALSE;
-               }
-       }
-       
-       /* Do not touch BBs following a switch (they are the "default" branch) */
-       if ((previous_bb->last_ins != NULL) && (previous_bb->last_ins->opcode == OP_SWITCH)) {
-               return FALSE;
-       }
-       
-       /* Do not touch BBs following the entry BB and jumping to something that is not */
-       /* thiry "next" bb (the entry BB cannot contain the branch) */
-       if ((previous_bb == cfg->bb_entry) && (bb->next_bb != target_bb)) {
-               return FALSE;
-       }
-
-       /* 
-        * Do not touch BBs following a try block as the code in 
-        * mini_method_compile needs them to compute the length of the try block.
-        */
-       if (MONO_BBLOCK_IS_IN_REGION (previous_bb, MONO_REGION_TRY))
-               return FALSE;
-       
-       /* Check that there is a target BB, and that bb is not an empty loop (Bug 75061) */
-       if ((target_bb != NULL) && (target_bb != bb)) {
-               int i;
-
-               if (cfg->verbose_level > 1) {
-                       printf ("remove_block_if_useless, removed BB%d\n", bb->block_num);
-               }
-               
-               /* unlink_bblock () modifies the bb->in_bb array so can't use a for loop here */
-               while (bb->in_count) {
-                       MonoBasicBlock *in_bb = bb->in_bb [0];
-                       mono_unlink_bblock (cfg, in_bb, bb);
-                       link_bblock (cfg, in_bb, target_bb);
-                       replace_out_block_in_code (in_bb, bb, target_bb);
-               }
-               
-               mono_unlink_bblock (cfg, bb, target_bb);
-               
-               if ((previous_bb != cfg->bb_entry) &&
-                               (previous_bb->region == bb->region) &&
-                               ((previous_bb->last_ins == NULL) ||
-                               ((previous_bb->last_ins->opcode != OP_BR) &&
-                               (! (MONO_IS_COND_BRANCH_OP (previous_bb->last_ins))) &&
-                               (previous_bb->last_ins->opcode != OP_SWITCH)))) {
-                       for (i = 0; i < previous_bb->out_count; i++) {
-                               if (previous_bb->out_bb [i] == target_bb) {
-                                       MonoInst *jump;
-                                       MONO_INST_NEW (cfg, jump, OP_BR);
-                                       MONO_ADD_INS (previous_bb, jump);
-                                       jump->cil_code = previous_bb->cil_code;
-                                       jump->inst_target_bb = target_bb;
-                                       break;
-                               }
-                       }
-               }
-               
-               previous_bb->next_bb = bb->next_bb;
-               nullify_basic_block (bb);
-               
-               return TRUE;
-       } else {
-               return FALSE;
-       }
-}
-
-void
-mono_merge_basic_blocks (MonoCompile *cfg, MonoBasicBlock *bb, MonoBasicBlock *bbn) 
-{
-       MonoInst *inst;
-       MonoBasicBlock *prev_bb;
-       int i;
-
-       bb->has_array_access |= bbn->has_array_access;
-       bb->extended |= bbn->extended;
-
-       mono_unlink_bblock (cfg, bb, bbn);
-       for (i = 0; i < bbn->out_count; ++i)
-               mono_link_bblock (cfg, bb, bbn->out_bb [i]);
-       while (bbn->out_count)
-               mono_unlink_bblock (cfg, bbn, bbn->out_bb [0]);
-
-       /* Handle the branch at the end of the bb */
-       for (inst = bb->code; inst != NULL; inst = inst->next) {
-               if (inst->opcode == OP_CALL_HANDLER) {
-                       g_assert (inst->inst_target_bb == bbn);
-                       NULLIFY_INS (inst);
-               }
-               if (MONO_IS_JUMP_TABLE (inst)) {
-                       int i;
-                       MonoJumpInfoBBTable *table = MONO_JUMP_TABLE_FROM_INS (inst);
-                       for (i = 0; i < table->table_size; i++ ) {
-                               /* Might be already NULL from a previous merge */
-                               if (table->table [i])
-                                       g_assert (table->table [i] == bbn);
-                               table->table [i] = NULL;
-                       }
-                       /* Can't nullify this as later instructions depend on it */
-               }
-       }
-       if (bb->last_ins && MONO_IS_COND_BRANCH_OP (bb->last_ins)) {
-               g_assert (bb->last_ins->inst_false_bb == bbn);
-               bb->last_ins->inst_false_bb = NULL;
-               bb->extended = TRUE;
-       } else if (bb->last_ins && MONO_IS_BRANCH_OP (bb->last_ins)) {
-               NULLIFY_INS (bb->last_ins);
-       }
-
-       if (bb->last_ins) {
-               if (bbn->code) {
-                       bb->last_ins->next = bbn->code;
-                       bbn->code->prev = bb->last_ins;
-                       bb->last_ins = bbn->last_ins;
-               }
-       } else {
-               bb->code = bbn->code;
-               bb->last_ins = bbn->last_ins;
-       }
-       for (prev_bb = cfg->bb_entry; prev_bb && prev_bb->next_bb != bbn; prev_bb = prev_bb->next_bb)
-               ;
-       if (prev_bb) {
-               prev_bb->next_bb = bbn->next_bb;
-       } else {
-               /* bbn might not be in the bb list yet */
-               if (bb->next_bb == bbn)
-                       bb->next_bb = bbn->next_bb;
-       }
-       nullify_basic_block (bbn);
-}
-
-static void
-move_basic_block_to_end (MonoCompile *cfg, MonoBasicBlock *bb)
-{
-       MonoBasicBlock *bbn, *next;
-
-       next = bb->next_bb;
-
-       /* Find the previous */
-       for (bbn = cfg->bb_entry; bbn->next_bb && bbn->next_bb != bb; bbn = bbn->next_bb)
-               ;
-       if (bbn->next_bb) {
-               bbn->next_bb = bb->next_bb;
-       }
-
-       /* Find the last */
-       for (bbn = cfg->bb_entry; bbn->next_bb; bbn = bbn->next_bb)
-               ;
-       bbn->next_bb = bb;
-       bb->next_bb = NULL;
-
-       /* Add a branch */
-       if (next && (!bb->last_ins || ((bb->last_ins->opcode != OP_NOT_REACHED) && (bb->last_ins->opcode != OP_BR) && (bb->last_ins->opcode != OP_BR_REG) && (!MONO_IS_COND_BRANCH_OP (bb->last_ins))))) {
-               MonoInst *ins;
-
-               MONO_INST_NEW (cfg, ins, OP_BR);
-               MONO_ADD_INS (bb, ins);
-               link_bblock (cfg, bb, next);
-               ins->inst_target_bb = next;
-       }               
-}
-
-/*
- * mono_remove_block:
- *
- *   Remove BB from the control flow graph
- */
-void
-mono_remove_bblock (MonoCompile *cfg, MonoBasicBlock *bb) 
-{
-       MonoBasicBlock *tmp_bb;
-
-       for (tmp_bb = cfg->bb_entry; tmp_bb && tmp_bb->next_bb != bb; tmp_bb = tmp_bb->next_bb)
-               ;
-
-       g_assert (tmp_bb);
-       tmp_bb->next_bb = bb->next_bb;
-}
-
-/* checks that a and b represent the same instructions, conservatively,
- * it can return FALSE also for two trees that are equal.
- * FIXME: also make sure there are no side effects.
- */
-static int
-same_trees (MonoInst *a, MonoInst *b)
-{
-       int arity;
-       if (a->opcode != b->opcode)
-               return FALSE;
-       arity = mono_burg_arity [a->opcode];
-       if (arity == 1) {
-               if (a->ssa_op == b->ssa_op && a->ssa_op == MONO_SSA_LOAD && a->inst_i0 == b->inst_i0)
-                       return TRUE;
-               return same_trees (a->inst_left, b->inst_left);
-       } else if (arity == 2) {
-               return same_trees (a->inst_left, b->inst_left) && same_trees (a->inst_right, b->inst_right);
-       } else if (arity == 0) {
-               switch (a->opcode) {
-               case OP_ICONST:
-                       return a->inst_c0 == b->inst_c0;
-               default:
-                       return FALSE;
-               }
-       }
-       return FALSE;
-}
-
-static int
-get_unsigned_condbranch (int opcode)
-{
-       switch (opcode) {
-       case CEE_BLE: return CEE_BLE_UN;
-       case CEE_BLT: return CEE_BLT_UN;
-       case CEE_BGE: return CEE_BGE_UN;
-       case CEE_BGT: return CEE_BGT_UN;
-       }
-       g_assert_not_reached ();
-       return 0;
-}
-
-static int
-tree_is_unsigned (MonoInst* ins) {
-       switch (ins->opcode) {
-       case OP_ICONST:
-               return (int)ins->inst_c0 >= 0;
-       /* array lengths are positive as are string sizes */
-       case CEE_LDLEN:
-       case OP_STRLEN:
-               return TRUE;
-       case CEE_CONV_U1:
-       case CEE_CONV_U2:
-       case CEE_CONV_U4:
-       case CEE_CONV_OVF_U1:
-       case CEE_CONV_OVF_U2:
-       case CEE_CONV_OVF_U4:
-               return TRUE;
-       case CEE_LDIND_U1:
-       case CEE_LDIND_U2:
-       case CEE_LDIND_U4:
-               return TRUE;
-       default:
-               return FALSE;
-       }
-}
-
-/* check if an unsigned compare can be used instead of two signed compares
- * for (val < 0 || val > limit) conditionals.
- * Returns TRUE if the optimization has been applied.
- * Note that this can't be applied if the second arg is not positive...
- */
-static int
-try_unsigned_compare (MonoCompile *cfg, MonoBasicBlock *bb)
-{
-       MonoBasicBlock *truet, *falset;
-       MonoInst *cmp_inst = bb->last_ins->inst_left;
-       MonoInst *condb;
-       if (!cmp_inst->inst_right->inst_c0 == 0)
-               return FALSE;
-       truet = bb->last_ins->inst_true_bb;
-       falset = bb->last_ins->inst_false_bb;
-       if (falset->in_count != 1)
-               return FALSE;
-       condb = falset->last_ins;
-       /* target bb must have one instruction */
-       if (!condb || (condb != falset->code))
-               return FALSE;
-       if ((((condb->opcode == CEE_BLE || condb->opcode == CEE_BLT) && (condb->inst_false_bb == truet))
-                       || ((condb->opcode == CEE_BGE || condb->opcode == CEE_BGT) && (condb->inst_true_bb == truet)))
-                       && same_trees (cmp_inst->inst_left, condb->inst_left->inst_left)) {
-               if (!tree_is_unsigned (condb->inst_left->inst_right))
-                       return FALSE;
-               condb->opcode = get_unsigned_condbranch (condb->opcode);
-               /* change the original condbranch to just point to the new unsigned check */
-               bb->last_ins->opcode = OP_BR;
-               bb->last_ins->inst_target_bb = falset;
-               replace_out_block (bb, truet, NULL);
-               replace_in_block (truet, bb, NULL);
-               return TRUE;
-       }
-       return FALSE;
-}
-
-/*
- * Optimizes the branches on the Control Flow Graph
- *
- */
-void
-mono_optimize_branches (MonoCompile *cfg)
-{
-       int i, changed = FALSE;
-       MonoBasicBlock *bb, *bbn;
-       guint32 niterations;
-
-       /*
-        * Some crazy loops could cause the code below to go into an infinite
-        * loop, see bug #53003 for an example. To prevent this, we put an upper
-        * bound on the number of iterations.
-        */
-       if (cfg->num_bblocks > 1000)
-               niterations = cfg->num_bblocks * 2;
-       else
-               niterations = 1000;
-       
-       do {
-               MonoBasicBlock *previous_bb;
-               changed = FALSE;
-               niterations --;
-
-               /* we skip the entry block (exit is handled specially instead ) */
-               for (previous_bb = cfg->bb_entry, bb = cfg->bb_entry->next_bb; bb; previous_bb = bb, bb = bb->next_bb) {
-                       /* dont touch code inside exception clauses */
-                       if (bb->region != -1)
-                               continue;
-
-                       if (!bb->not_useless && remove_block_if_useless (cfg, bb, previous_bb)) {
-                               changed = TRUE;
-                               continue;
-                       }
-
-                       if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) {
-                               if (cfg->verbose_level > 2)
-                                       g_print ("nullify block triggered %d\n", bbn->block_num);
-
-                               bb->next_bb = bbn->next_bb;
-
-                               for (i = 0; i < bbn->out_count; i++)
-                                       replace_in_block (bbn->out_bb [i], bbn, NULL);
-
-                               nullify_basic_block (bbn);                      
-                               changed = TRUE;
-                       }
-
-                       if (bb->out_count == 1) {
-                               bbn = bb->out_bb [0];
-
-                               /* conditional branches where true and false targets are the same can be also replaced with OP_BR */
-                               if (bb->last_ins && (bb->last_ins->opcode != OP_BR) && MONO_IS_COND_BRANCH_OP (bb->last_ins)) {
-                                       if (!cfg->new_ir) {
-                                               MonoInst *pop;
-                                               MONO_INST_NEW (cfg, pop, CEE_POP);
-                                               pop->inst_left = bb->last_ins->inst_left->inst_left;
-                                               mono_add_ins_to_end (bb, pop);
-                                               MONO_INST_NEW (cfg, pop, CEE_POP);
-                                               pop->inst_left = bb->last_ins->inst_left->inst_right;
-                                               mono_add_ins_to_end (bb, pop);
-                                       }
-                                       bb->last_ins->opcode = OP_BR;
-                                       bb->last_ins->inst_target_bb = bb->last_ins->inst_true_bb;
-                                       changed = TRUE;
-                                       if (cfg->verbose_level > 2)
-                                               g_print ("cond branch removal triggered in %d %d\n", bb->block_num, bb->out_count);
-                               }
-
-                               if (bb->region == bbn->region && bb->next_bb == bbn) {
-                                       /* the block are in sequence anyway ... */
-
-                                       /* branches to the following block can be removed */
-                                       if (bb->last_ins && bb->last_ins->opcode == OP_BR) {
-                                               bb->last_ins->opcode = OP_NOP;
-                                               changed = TRUE;
-                                               if (cfg->verbose_level > 2)
-                                                       g_print ("br removal triggered %d -> %d\n", bb->block_num, bbn->block_num);
-                                       }
-
-                                       if (bbn->in_count == 1 && !bb->extended) {
-                                               if (bbn != cfg->bb_exit) {
-                                                       if (cfg->verbose_level > 2)
-                                                               g_print ("block merge triggered %d -> %d\n", bb->block_num, bbn->block_num);
-                                                       mono_merge_basic_blocks (cfg, bb, bbn);
-                                                       changed = TRUE;
-                                                       continue;
-                                               }
-
-                                               //mono_print_bb_code (bb);
-                                       }
-                               }
-                       }
-
-                       if ((bbn = bb->next_bb) && bbn->in_count == 0 && bb->region == bbn->region) {
-                               if (cfg->verbose_level > 2) {
-                                       g_print ("nullify block triggered %d\n", bbn->block_num);
-                               }
-                               bb->next_bb = bbn->next_bb;
-
-                               for (i = 0; i < bbn->out_count; i++)
-                                       replace_in_block (bbn->out_bb [i], bbn, NULL);
-
-                               nullify_basic_block (bbn);                      
-                               changed = TRUE;
-                               continue;
-                       }
-
-                       if (bb->out_count == 1) {
-                               bbn = bb->out_bb [0];
-
-                               if (bb->last_ins && bb->last_ins->opcode == OP_BR) {
-                                       bbn = bb->last_ins->inst_target_bb;
-                                       if (bb->region == bbn->region && bbn->code && bbn->code->opcode == OP_BR &&
-                                           bbn->code->inst_target_bb->region == bb->region) {
-                                               
-                                               if (cfg->verbose_level > 2)
-                                                       g_print ("branch to branch triggered %d -> %d -> %d\n", bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num);
-
-                                               replace_in_block (bbn, bb, NULL);
-                                               replace_out_block (bb, bbn, bbn->code->inst_target_bb);
-                                               link_bblock (cfg, bb, bbn->code->inst_target_bb);
-                                               bb->last_ins->inst_target_bb = bbn->code->inst_target_bb;
-                                               changed = TRUE;
-                                               continue;
-                                       }
-                               }
-                       } else if (bb->out_count == 2) {
-                               if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) {
-                                       int branch_result;
-                                       MonoBasicBlock *taken_branch_target = NULL, *untaken_branch_target = NULL;
-
-                                       if (cfg->new_ir) {
-                                               if (bb->last_ins->flags & MONO_INST_CFOLD_TAKEN)
-                                                       branch_result = BRANCH_TAKEN;
-                                               else if (bb->last_ins->flags & MONO_INST_CFOLD_NOT_TAKEN)
-                                                       branch_result = BRANCH_NOT_TAKEN;
-                                               else
-                                                       branch_result = BRANCH_UNDEF;
-                                       }
-                                       else
-                                               branch_result = mono_eval_cond_branch (bb->last_ins);
-
-                                       if (branch_result == BRANCH_TAKEN) {
-                                               taken_branch_target = bb->last_ins->inst_true_bb;
-                                               untaken_branch_target = bb->last_ins->inst_false_bb;
-                                       } else if (branch_result == BRANCH_NOT_TAKEN) {
-                                               taken_branch_target = bb->last_ins->inst_false_bb;
-                                               untaken_branch_target = bb->last_ins->inst_true_bb;
-                                       }
-                                       if (taken_branch_target) {
-                                               /* if mono_eval_cond_branch () is ever taken to handle 
-                                                * non-constant values to compare, issue a pop here.
-                                                */
-                                               bb->last_ins->opcode = OP_BR;
-                                               bb->last_ins->inst_target_bb = taken_branch_target;
-                                               if (!bb->extended)
-                                                       mono_unlink_bblock (cfg, bb, untaken_branch_target);
-                                               changed = TRUE;
-                                               continue;
-                                       }
-                                       bbn = bb->last_ins->inst_true_bb;
-                                       if (bb->region == bbn->region && bbn->code && bbn->code->opcode == OP_BR &&
-                                           bbn->code->inst_target_bb->region == bb->region) {
-                                               if (cfg->verbose_level > 2)             
-                                                       g_print ("cbranch1 to branch triggered %d -> (%d) %d (0x%02x)\n", 
-                                                                bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num, 
-                                                                bbn->code->opcode);
-
-                                               /* 
-                                                * Unlink, then relink bblocks to avoid various
-                                                * tricky situations when the two targets of the branch
-                                                * are equal, or will become equal after the change.
-                                                */
-                                               mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
-                                               mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
-
-                                               bb->last_ins->inst_true_bb = bbn->code->inst_target_bb;
-
-                                               link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
-                                               link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
-
-                                               changed = TRUE;
-                                               continue;
-                                       }
-
-                                       bbn = bb->last_ins->inst_false_bb;
-                                       if (bbn && bb->region == bbn->region && bbn->code && bbn->code->opcode == OP_BR &&
-                                           bbn->code->inst_target_bb->region == bb->region) {
-                                               if (cfg->verbose_level > 2)
-                                                       g_print ("cbranch2 to branch triggered %d -> (%d) %d (0x%02x)\n", 
-                                                                bb->block_num, bbn->block_num, bbn->code->inst_target_bb->block_num, 
-                                                                bbn->code->opcode);
-
-                                               mono_unlink_bblock (cfg, bb, bb->last_ins->inst_true_bb);
-                                               mono_unlink_bblock (cfg, bb, bb->last_ins->inst_false_bb);
-
-                                               bb->last_ins->inst_false_bb = bbn->code->inst_target_bb;
-
-                                               link_bblock (cfg, bb, bb->last_ins->inst_true_bb);
-                                               link_bblock (cfg, bb, bb->last_ins->inst_false_bb);
-
-                                               changed = TRUE;
-                                               continue;
-                                       }
-
-                                       bbn = bb->last_ins->inst_false_bb;
-                                       /*
-                                        * If bb is an extended bb, it could contain an inside branch to bbn.
-                                        * FIXME: Enable the optimization if that is not true.
-                                        * If bblocks_linked () is true, then merging bb and bbn
-                                        * would require addition of an extra branch at the end of bbn 
-                                        * slowing down loops.
-                                        */
-                                       if (cfg->new_ir && bbn && bb->region == bbn->region && bbn->in_count == 1 && cfg->enable_extended_bblocks && bbn != cfg->bb_exit && !bb->extended && !bbn->out_of_line && !mono_bblocks_linked (bbn, bb)) {
-                                               g_assert (bbn->in_bb [0] == bb);
-                                               if (cfg->verbose_level > 2)
-                                                       g_print ("merge false branch target triggered BB%d -> BB%d\n", bb->block_num, bbn->block_num);
-                                               mono_merge_basic_blocks (cfg, bb, bbn);
-                                               changed = TRUE;
-                                               continue;
-                                       }
-                               }
-
-                               /* detect and optimize to unsigned compares checks like: if (v < 0 || v > limit */
-                               if (bb->last_ins && bb->last_ins->opcode == CEE_BLT && !cfg->new_ir && bb->last_ins->inst_left->inst_right->opcode == OP_ICONST) {
-                                       if (try_unsigned_compare (cfg, bb)) {
-                                               /*g_print ("applied in bb %d (->%d) %s\n", bb->block_num, bb->last_ins->inst_target_bb->block_num, mono_method_full_name (cfg->method, TRUE));*/
-                                               changed = TRUE;
-                                               continue;
-                                       }
-                               }
-
-                               if (bb->last_ins && MONO_IS_COND_BRANCH_NOFP (bb->last_ins)) {
-                                       if (bb->last_ins->inst_false_bb && bb->last_ins->inst_false_bb->out_of_line && (bb->region == bb->last_ins->inst_false_bb->region)) {
-                                               /* Reverse the branch */
-                                               bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode);
-                                               bbn = bb->last_ins->inst_false_bb;
-                                               bb->last_ins->inst_false_bb = bb->last_ins->inst_true_bb;
-                                               bb->last_ins->inst_true_bb = bbn;
-
-                                               move_basic_block_to_end (cfg, bb->last_ins->inst_true_bb);
-                                               if (cfg->verbose_level > 2)
-                                                       g_print ("cbranch to throw block triggered %d.\n", 
-                                                                        bb->block_num);
-                                       }
-                               }
-                       }
-               }
-       } while (changed && (niterations > 0));
-}
-
 static void
 mono_compile_create_vars (MonoCompile *cfg)
 {
@@ -12490,7 +11819,7 @@ mini_select_instructions (MonoCompile *cfg)
                                bb->last_ins->inst_true_bb = bb->last_ins->inst_false_bb;
                                bb->last_ins->inst_false_bb = tmp;
 
-                               bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode);
+                               bb->last_ins->opcode = mono_reverse_branch_op (bb->last_ins->opcode);
                        } else {                        
                                MonoInst *ins;
 
@@ -12746,6 +12075,11 @@ mono_codegen (MonoCompile *cfg)
                switch (patch_info->type) {
                case MONO_PATCH_INFO_ABS: {
                        MonoJitICallInfo *info = mono_find_jit_icall_by_addr (patch_info->data.target);
+
+                       /*
+                        * Change patches of type MONO_PATCH_INFO_ABS into patches describing the 
+                        * absolute address.
+                        */
                        if (info) {
                                //printf ("TEST %s %p\n", info->name, patch_info->data.target);
                                // FIXME: CLEAN UP THIS MESS.
@@ -12771,19 +12105,33 @@ mono_codegen (MonoCompile *cfg)
                                        }
                                }
                        }
-                       else {
+                       
+                       if (patch_info->type == MONO_PATCH_INFO_ABS && !cfg->new_ir) {
                                MonoVTable *vtable = mono_find_class_init_trampoline_by_addr (patch_info->data.target);
                                if (vtable) {
                                        patch_info->type = MONO_PATCH_INFO_CLASS_INIT;
                                        patch_info->data.klass = vtable->klass;
-                               } else {
-                                       MonoClass *klass = mono_find_delegate_trampoline_by_addr (patch_info->data.target);
-                                       if (klass) {
-                                               patch_info->type = MONO_PATCH_INFO_DELEGATE_TRAMPOLINE;
-                                               patch_info->data.klass = klass;
+                               }
+                       }
+
+                       if (patch_info->type == MONO_PATCH_INFO_ABS) {
+                               MonoClass *klass = mono_find_delegate_trampoline_by_addr (patch_info->data.target);
+                               if (klass) {
+                                       patch_info->type = MONO_PATCH_INFO_DELEGATE_TRAMPOLINE;
+                                       patch_info->data.klass = klass;
+                               }
+                       }
+
+                       if (patch_info->type == MONO_PATCH_INFO_ABS) {
+                               if (cfg->abs_patches) {
+                                       MonoJumpInfo *abs_ji = g_hash_table_lookup (cfg->abs_patches, patch_info->data.target);
+                                       if (abs_ji) {
+                                               patch_info->type = abs_ji->type;
+                                               patch_info->data.target = abs_ji->data.target;
                                        }
                                }
                        }
+
                        break;
                }
                case MONO_PATCH_INFO_SWITCH: {
@@ -12881,135 +12229,6 @@ if (valgrind_register){
 #endif
 }
 
-void
-mono_remove_critical_edges (MonoCompile *cfg)
-{
-       MonoBasicBlock *bb;
-       MonoBasicBlock *previous_bb;
-       
-       if (cfg->verbose_level > 3) {
-               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
-                       int i;
-                       printf ("remove_critical_edges, BEFORE BB%d (in:", bb->block_num);
-                       for (i = 0; i < bb->in_count; i++) {
-                               printf (" %d", bb->in_bb [i]->block_num);
-                       }
-                       printf (") (out:");
-                       for (i = 0; i < bb->out_count; i++) {
-                               printf (" %d", bb->out_bb [i]->block_num);
-                       }
-                       printf (")");
-                       if (bb->last_ins != NULL) {
-                               printf (" ");
-                               mono_print_tree (bb->last_ins);
-                       }
-                       printf ("\n");
-               }
-       }
-       
-       for (previous_bb = cfg->bb_entry, bb = previous_bb->next_bb; bb != NULL; previous_bb = previous_bb->next_bb, bb = bb->next_bb) {
-               if (bb->in_count > 1) {
-                       int in_bb_index;
-                       for (in_bb_index = 0; in_bb_index < bb->in_count; in_bb_index++) {
-                               MonoBasicBlock *in_bb = bb->in_bb [in_bb_index];
-                               if (in_bb->out_count > 1) {
-                                       MonoBasicBlock *new_bb = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
-                                       new_bb->block_num = cfg->num_bblocks++;
-//                                     new_bb->real_offset = bb->real_offset;
-                                       new_bb->region = bb->region;
-                                       
-                                       /* Do not alter the CFG while altering the BB list */
-                                       if (previous_bb->region == bb->region) {
-                                               if (previous_bb != cfg->bb_entry) {
-                                                       /* If previous_bb "followed through" to bb, */
-                                                       /* keep it linked with a OP_BR */
-                                                       if ((previous_bb->last_ins == NULL) ||
-                                                                       ((previous_bb->last_ins->opcode != OP_BR) &&
-                                                                       (! (MONO_IS_COND_BRANCH_OP (previous_bb->last_ins))) &&
-                                                                       (previous_bb->last_ins->opcode != OP_SWITCH))) {
-                                                               int i;
-                                                               /* Make sure previous_bb really falls through bb */
-                                                               for (i = 0; i < previous_bb->out_count; i++) {
-                                                                       if (previous_bb->out_bb [i] == bb) {
-                                                                               MonoInst *jump;
-                                                                               MONO_INST_NEW (cfg, jump, OP_BR);
-                                                                               MONO_ADD_INS (previous_bb, jump);
-                                                                               jump->cil_code = previous_bb->cil_code;
-                                                                               jump->inst_target_bb = bb;
-                                                                               break;
-                                                                       }
-                                                               }
-                                                       }
-                                               } else {
-                                                       /* We cannot add any inst to the entry BB, so we must */
-                                                       /* put a new BB in the middle to hold the OP_BR */
-                                                       MonoInst *jump;
-                                                       MonoBasicBlock *new_bb_after_entry = mono_mempool_alloc0 ((cfg)->mempool, sizeof (MonoBasicBlock));
-                                                       new_bb_after_entry->block_num = cfg->num_bblocks++;
-//                                                     new_bb_after_entry->real_offset = bb->real_offset;
-                                                       new_bb_after_entry->region = bb->region;
-                                                       
-                                                       MONO_INST_NEW (cfg, jump, OP_BR);
-                                                       MONO_ADD_INS (new_bb_after_entry, jump);
-                                                       jump->cil_code = bb->cil_code;
-                                                       jump->inst_target_bb = bb;
-                                                       
-                                                       previous_bb->next_bb = new_bb_after_entry;
-                                                       previous_bb = new_bb_after_entry;
-                                                       
-                                                       if (cfg->verbose_level > 2) {
-                                                               printf ("remove_critical_edges, added helper BB%d jumping to BB%d\n", new_bb_after_entry->block_num, bb->block_num);
-                                                       }
-                                               }
-                                       }
-                                       
-                                       /* Insert new_bb in the BB list */
-                                       previous_bb->next_bb = new_bb;
-                                       new_bb->next_bb = bb;
-                                       previous_bb = new_bb;
-                                       
-                                       /* Setup in_bb and out_bb */
-                                       new_bb->in_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
-                                       new_bb->in_bb [0] = in_bb;
-                                       new_bb->in_count = 1;
-                                       new_bb->out_bb = mono_mempool_alloc ((cfg)->mempool, sizeof (MonoBasicBlock*));
-                                       new_bb->out_bb [0] = bb;
-                                       new_bb->out_count = 1;
-                                       
-                                       /* Relink in_bb and bb to (from) new_bb */
-                                       replace_out_block (in_bb, bb, new_bb);
-                                       replace_out_block_in_code (in_bb, bb, new_bb);
-                                       replace_in_block (bb, in_bb, new_bb);
-                                       
-                                       if (cfg->verbose_level > 2) {
-                                               printf ("remove_critical_edges, removed critical edge from BB%d to BB%d (added BB%d)\n", in_bb->block_num, bb->block_num, new_bb->block_num);
-                                       }
-                               }
-                       }
-               }
-       }
-       
-       if (cfg->verbose_level > 3) {
-               for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
-                       int i;
-                       printf ("remove_critical_edges, AFTER BB%d (in:", bb->block_num);
-                       for (i = 0; i < bb->in_count; i++) {
-                               printf (" %d", bb->in_bb [i]->block_num);
-                       }
-                       printf (") (out:");
-                       for (i = 0; i < bb->out_count; i++) {
-                               printf (" %d", bb->out_bb [i]->block_num);
-                       }
-                       printf (")");
-                       if (bb->last_ins != NULL) {
-                               printf (" ");
-                               mono_print_tree (bb->last_ins);
-                       }
-                       printf ("\n");
-               }
-       }
-}
-
 static MonoGenericInst*
 get_object_generic_inst (int type_argc)
 {
@@ -13106,6 +12325,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
        cfg->verbose_level = mini_verbose;
        cfg->compile_aot = compile_aot;
        cfg->skip_visibility = method->skip_visibility;
+       cfg->orig_method = method;
        if (try_generic_shared)
                cfg->generic_sharing_context = (MonoGenericSharingContext*)&cfg->generic_sharing_context;
        cfg->token_info_hash = g_hash_table_new (NULL, NULL);
@@ -13339,7 +12559,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
                                        g_print ("found unreachable code in BB%d\n", bbn->block_num);
                                /* There may exist unreachable branches to this bb */
                                bb->next_bb = bbn->next_bb;
-                               nullify_basic_block (bbn);                      
+                               mono_nullify_basic_block (bbn);                 
                        } else {
                                bb = bb->next_bb;
                        }
@@ -13610,7 +12830,7 @@ mini_method_compile (MonoMethod *method, guint32 opts, MonoDomain *domain, gbool
                                        bb->last_ins->inst_true_bb = bb->last_ins->inst_false_bb;
                                        bb->last_ins->inst_false_bb = tmp;
 
-                                       bb->last_ins->opcode = reverse_branch_op (bb->last_ins->opcode);
+                                       bb->last_ins->opcode = mono_reverse_branch_op (bb->last_ins->opcode);
                                } else {                        
                                        MonoInst *inst = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
                                        inst->opcode = OP_BR;