Exemplo n.º 1
0
static int
label_instructions (MonoCompile *cfg)
{
	MonoBasicBlock *bb;
	int instruction_count = 0;

	for (bb = cfg->bb_entry; bb; bb = bb->next_bb) {
		cfg_debug ("bb: %d (in: %d, out: %d)", bb->block_num, bb->in_count, bb->out_count);
		MonoInst *insn;
		for (insn = bb->code; insn; insn = insn->next) {
			instruction_count++;
			void *id = g_hash_table_lookup (cfg->gdump_ctx->insn2id, insn);
			if (id != NULL) // already in the table.
				continue;
			int *new_id = (int *) mono_mempool_alloc0 (cfg->mempool, sizeof (int));
			*new_id = cfg->gdump_ctx->next_insn_id++;
			g_hash_table_insert (cfg->gdump_ctx->insn2id, insn, new_id);
#ifdef CFG_DEBUG
			GString *insndesc = mono_print_ins_index_strbuf (-1, insn);
			cfg_debug ("> insn%002d: %s", *new_id, insndesc->str);
			g_string_free (insndesc, TRUE);
#endif
		}
	}
	return instruction_count;
}
Exemplo n.º 2
0
static ConstantPoolEntry*
create_cp_entry (MonoCompile *cfg, void *data, pool_type pt)
{
	ConstantPoolEntry *entry = (ConstantPoolEntry *) mono_mempool_alloc0 (cfg->mempool, sizeof (ConstantPoolEntry));
	entry->pt = pt;
	entry->data = data;
	return entry;
}
Exemplo n.º 3
0
static void
recursively_make_pred_seq_points (MonoCompile *cfg, MonoBasicBlock *bb)
{
	const gpointer MONO_SEQ_SEEN_LOOP = GINT_TO_POINTER(-1);

	GArray *predecessors = g_array_new (FALSE, TRUE, sizeof (gpointer));
	GHashTable *seen = g_hash_table_new_full (g_direct_hash, NULL, NULL, NULL);

	// Insert/remove sentinel into the memoize table to detect loops containing bb
	bb->pred_seq_points = (MonoInst**)MONO_SEQ_SEEN_LOOP;

	for (int i = 0; i < bb->in_count; ++i) {
		MonoBasicBlock *in_bb = bb->in_bb [i];
		
		// This bb has the last seq point, append it and continue
		if (in_bb->last_seq_point != NULL) {
			predecessors = g_array_append_val (predecessors, in_bb->last_seq_point);
			continue;
		}

		// We've looped or handled this before, exit early.
		// No last sequence points to find.
		if (in_bb->pred_seq_points == MONO_SEQ_SEEN_LOOP)
			continue;

		// Take sequence points from incoming basic blocks
	
		if (in_bb == cfg->bb_entry)
			continue;

		if (in_bb->pred_seq_points == NULL)
			recursively_make_pred_seq_points (cfg, in_bb);

		// Union sequence points with incoming bb's
		for (int i=0; i < in_bb->num_pred_seq_points; i++) {
			if (!g_hash_table_lookup (seen, in_bb->pred_seq_points [i])) {
				g_array_append_val (predecessors, in_bb->pred_seq_points [i]);
				g_hash_table_insert (seen, in_bb->pred_seq_points [i], (gpointer)&MONO_SEQ_SEEN_LOOP);
			}
		}
		// predecessors = g_array_append_vals (predecessors, in_bb->pred_seq_points, in_bb->num_pred_seq_points);
	}

	g_hash_table_destroy (seen);

	if (predecessors->len != 0) {
		bb->pred_seq_points = (MonoInst **)mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst *) * predecessors->len);
		bb->num_pred_seq_points = predecessors->len;

		for (int newer = 0; newer < bb->num_pred_seq_points; newer++) {
			bb->pred_seq_points [newer] = g_array_index(predecessors, MonoInst*, newer);
		}
	} 
Exemplo n.º 4
0
void
mono_cfg_dump_begin_group (MonoCompile *cfg)
{
	if (cfg->gdump_ctx == NULL)
		return;
	write_byte (cfg, BEGIN_GROUP);
	char *title = (char *) mono_mempool_alloc0 (cfg->mempool, 0x2000);
	sprintf (title, "%s::%s", m_class_get_name (cfg->method->klass), cfg->method->name);
	write_pool (cfg, create_cp_entry (cfg, (void *) title, PT_STRING));
	write_pool (cfg, create_cp_entry (cfg, (void *) cfg->method->name, PT_STRING));
	write_pool (cfg, create_cp_entry (cfg, (void *) cfg->method, PT_METHOD));
	write_int (cfg, 0); // TODO: real bytecode index.
}
Exemplo n.º 5
0
static void
add_non_null (MonoVariableRelationsEvaluationArea *area, MonoCompile *cfg, int reg,
			  GSList **check_relations)
{
	MonoAdditionalVariableRelation *rel;

	rel = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation));
	rel->variable = reg;
	rel->relation.relation = MONO_GT_RELATION;
	rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
	rel->relation.related_value.value.constant.value = 0;

	apply_change_to_evaluation_area (area, rel);

	*check_relations = g_slist_append_mempool (cfg->mempool, *check_relations, rel);
}
Exemplo n.º 6
0
void mono_cfg_dump_create_context (MonoCompile *cfg)
{
	cfg->gdump_ctx = NULL;

	if (!cfg_dump_method_inited) {
		cfg_dump_method_name = g_getenv ("MONO_JIT_DUMP_METHOD");
		cfg_dump_method_inited = TRUE;
	}
	if (!cfg_dump_method_name)
		return;
	const char *name = cfg_dump_method_name;

	if ((strchr (name, '.') > name) || strchr (name, ':')) {
		MonoMethodDesc *desc = mono_method_desc_new (name, TRUE);
		gboolean failed = !mono_method_desc_full_match (desc, cfg->method);
		mono_method_desc_free (desc);
		if (failed)
			return;
	} else
		if (strcmp (cfg->method->name, name) != 0)
			return;

	g_debug ("cfg_dump: create context for \"%s::%s\"", m_class_get_name (cfg->method->klass), cfg->method->name);
	int fd = create_socket (DEFAULT_HOST, DEFAULT_PORT);
	if (fd < 0) {
		g_warning ("cfg_dump: couldn't create socket: %s::%d", DEFAULT_HOST, DEFAULT_PORT);
		return;
	}

	MonoGraphDumper *ctx = (MonoGraphDumper *) mono_mempool_alloc0 (cfg->mempool, sizeof (MonoGraphDumper));
	ctx->fd = fd;
	ctx->constant_pool = g_hash_table_new ((GHashFunc) constant_pool_hash, constant_pool_equal);
	ctx->insn2id = g_hash_table_new ((GHashFunc) instruction_hash, instruction_equal);
	ctx->next_cp_id = 1;
	ctx->next_insn_id = 0;

	cfg->gdump_ctx = ctx;
}
Exemplo n.º 7
0
static void
add_pool_entry (MonoCompile *cfg, ConstantPoolEntry *entry)
{
	int *cp_id= (int *) mono_mempool_alloc0 (cfg->mempool, sizeof (int));
	*cp_id = cfg->gdump_ctx->next_cp_id;
	g_hash_table_insert (cfg->gdump_ctx->constant_pool, entry, cp_id);
	write_byte (cfg, POOL_NEW);
	write_short (cfg, cfg->gdump_ctx->next_cp_id++);
	switch (entry->pt) {
		case PT_STRING:
			write_byte (cfg, POOL_STRING);
			write_string (cfg, (char *) entry->data);
			break;
		case PT_METHOD: {
			MonoMethod *method = (MonoMethod *) entry->data;
			write_byte (cfg, POOL_METHOD);
			write_pool (cfg, create_cp_entry (cfg, (void *) method->klass, PT_KLASS));
			write_pool (cfg, create_cp_entry (cfg, (void *) method->name, PT_STRING));
			write_pool (cfg, create_cp_entry (cfg, (void *) method->signature, PT_SIGNATURE));
			write_int (cfg, (int) method->flags);
			write_int (cfg, -1); // don't transmit bytecode.
			break;
		}
		case PT_KLASS: {
			MonoClass *klass = (MonoClass *) entry->data;
			write_byte (cfg, POOL_KLASS);
			write_string (cfg, m_class_get_name (klass));
			write_byte (cfg, KLASS);
			break;
		}
		case PT_SIGNATURE: {
			write_byte (cfg, POOL_SIGNATURE);
			MonoMethodSignature *sig = (MonoMethodSignature *) entry->data;
			write_short (cfg, sig->param_count);
			for (int i = 0; i < sig->param_count; i++) {
				GString *sbuf = g_string_new (NULL);
				mono_type_get_desc (sbuf, sig->params [i], TRUE);
				write_pool (cfg, create_cp_entry (cfg, (void *) sbuf->str, PT_STRING));
				g_string_free (sbuf, TRUE);
			}
			GString *sbuf = g_string_new (NULL);
			mono_type_get_desc (sbuf, sig->ret, TRUE);
			write_pool (cfg, create_cp_entry (cfg, (void *) sbuf->str, PT_STRING));
			g_string_free (sbuf, TRUE);
			break;
		}
		case PT_OPTYPE: {
			MonoInst *insn = (MonoInst *) entry->data;
			write_byte (cfg, POOL_NODE_CLASS);

			write_string (cfg, mono_inst_name (insn->opcode));
			GString *insndesc = mono_print_ins_index_strbuf (-1, insn);
			int len = strnlen (insndesc->str, 0x2000);
#define CUTOFF 40
			if (len > CUTOFF) {
				insndesc->str[CUTOFF] = '\0';
				insndesc->str[CUTOFF - 1] = '.';
				insndesc->str[CUTOFF - 2] = '.';
			}
			write_string (cfg, insndesc->str);
			if (len > CUTOFF)
				insndesc->str[CUTOFF] = ' ';
			g_string_free (insndesc, TRUE);

			// one predecessor
			write_short (cfg, 1);
			write_byte (cfg, 0);
			write_pool (cfg, create_cp_entry (cfg, (void *) "predecessor", PT_STRING));
			write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_INPUTTYPE));

			// make NUM_SUCCESSOR successor edges, not everyone will be used.
#define NUM_SUCCESSOR 5
			write_short (cfg, NUM_SUCCESSOR);
			for (int i = 0; i < NUM_SUCCESSOR; i++) {
				char *str = g_strdup ("successor1");
				str[9] = '0' + i;
				write_byte (cfg, 0);
				write_pool (cfg, create_cp_entry (cfg, (void *) str, PT_STRING));
			}

			break;
		}
		case PT_INPUTTYPE: {
			write_byte (cfg, POOL_ENUM);
			write_pool (cfg, create_cp_entry (cfg, (void *) NULL, PT_ENUMKLASS));
			write_int (cfg, 0);
			break;
		}
		case PT_ENUMKLASS: {
			write_byte (cfg, POOL_KLASS);
			write_string (cfg, "InputType");
			write_byte (cfg, ENUM_KLASS);
			write_int (cfg, 1);
			write_pool (cfg, create_cp_entry (cfg, (void *) "fixed", PT_STRING));
			break;
		}
	}
}
Exemplo n.º 8
0
/*
 * Process a BB removing bounds checks from array accesses.
 * It does the following (in sequence):
 * - Get the BB entry condition
 * - Add its relations to the relation graph in the evaluation area
 * - Process all the MonoInst trees in the BB
 * - Recursively process all the children BBs in the dominator tree
 * - Remove the relations previously added to the relation graph
 *
 * bb: the BB that must be processed
 * area: the current evaluation area (it contains the relation graph and
 *       memory for all the evaluation contexts is already allocated)
 */
static void
process_block (MonoCompile *cfg, MonoBasicBlock *bb, MonoVariableRelationsEvaluationArea *area) {
	int inst_index;
	MonoInst *ins;
	MonoAdditionalVariableRelationsForBB additional_relations;
	GSList *dominated_bb, *l;
	GSList *check_relations = NULL;
	
	if (TRACE_ABC_REMOVAL) {
		printf ("\nProcessing block %d [dfn %d]...\n", bb->block_num, bb->dfn);
	}

	if (bb->region != -1)
		return;

	get_relations_from_previous_bb (area, bb, &additional_relations);
	if (TRACE_ABC_REMOVAL) {
		if (additional_relations.relation1.relation.relation != MONO_ANY_RELATION) {
			printf ("Adding relation 1 on variable %d: ", additional_relations.relation1.variable);
			print_summarized_value_relation (&(additional_relations.relation1.relation));
			printf ("\n");
		}
		if (additional_relations.relation2.relation.relation != MONO_ANY_RELATION) {
			printf ("Adding relation 2 on variable %d: ", additional_relations.relation2.variable);
			print_summarized_value_relation (&(additional_relations.relation2.relation));
			printf ("\n");
		}
	}
	apply_change_to_evaluation_area (area, &(additional_relations.relation1));
	apply_change_to_evaluation_area (area, &(additional_relations.relation2));

	inst_index = 0;
	for (ins = bb->code; ins; ins = ins->next) {
		MonoAdditionalVariableRelation *rel;
		int array_var, index_var;

		if (TRACE_ABC_REMOVAL) {
			printf ("Processing instruction %d\n", inst_index);
			inst_index++;
		}

		if (ins->opcode == OP_BOUNDS_CHECK) { /* Handle OP_LDELEMA2D, too */
			if (TRACE_ABC_REMOVAL) {
				printf ("Attempting check removal...\n");
			}

			array_var = ins->sreg1;
			index_var = ins->sreg2;
		
			remove_abc_from_inst (ins, area);

			/* We can derive additional relations from the bounds check */
			if (ins->opcode != OP_NOP) {
				rel = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation));
				rel->variable = index_var;
				rel->relation.relation = MONO_LT_RELATION;
				rel->relation.related_value.type = MONO_VARIABLE_SUMMARIZED_VALUE;
				rel->relation.related_value.value.variable.variable = array_var;
				rel->relation.related_value.value.variable.delta = 0;

				apply_change_to_evaluation_area (area, rel);

				check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel);

				rel = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoAdditionalVariableRelation));
				rel->variable = index_var;
				rel->relation.relation = MONO_GE_RELATION;
				rel->relation.related_value.type = MONO_CONSTANT_SUMMARIZED_VALUE;
				rel->relation.related_value.value.constant.value = 0;

				apply_change_to_evaluation_area (area, rel);

				check_relations = g_slist_append_mempool (cfg->mempool, check_relations, rel);
			}
		}

		if (ins->opcode == OP_CHECK_THIS) {
			if (eval_non_null (area, ins->sreg1)) {
				if (REPORT_ABC_REMOVAL)
					printf ("ARRAY-ACCESS: removed check_this instruction.\n");
				NULLIFY_INS (ins);
			}
		}

		if (ins->opcode == OP_NOT_NULL)
			add_non_null (area, cfg, ins->sreg1, &check_relations);

		/* 
		 * This doesn't work because LLVM can move the non-faulting loads before the faulting
		 * ones (test_0_llvm_moving_faulting_loads ()).
		 * FIXME: This also doesn't work because abcrem equates an array with its length,
		 * so a = new int [100] implies a != null, but a = new int [0] doesn't.
		 */
#if 0
		/*
		 * Eliminate MONO_INST_FAULT flags if possible.
		 */
		if (COMPILE_LLVM (cfg) && (ins->opcode == OP_LDLEN ||
								   ins->opcode == OP_BOUNDS_CHECK ||
								   ins->opcode == OP_STRLEN ||
								   (MONO_IS_LOAD_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)) ||
								   (MONO_IS_STORE_MEMBASE (ins) && (ins->flags & MONO_INST_FAULT)))) {
			int reg;

			if (MONO_IS_STORE_MEMBASE (ins))
				reg = ins->inst_destbasereg;
			else if (MONO_IS_LOAD_MEMBASE (ins))
				reg = ins->inst_basereg;
			else
				reg = ins->sreg1;

			if (eval_non_null (area, reg)) {
				if (REPORT_ABC_REMOVAL)
					printf ("ARRAY-ACCESS: removed MONO_INST_FAULT flag.\n");
				ins->flags &= ~MONO_INST_FAULT;
			} else {
				add_non_null (area, cfg, reg, &check_relations);
			}
		}
#endif
	}	
	
	if (TRACE_ABC_REMOVAL) {
		printf ("Processing block %d [dfn %d] done.\n", bb->block_num, bb->dfn);
	}
	
	for (dominated_bb = bb->dominated; dominated_bb != NULL; dominated_bb = dominated_bb->next) {
		process_block (cfg, (MonoBasicBlock*) (dominated_bb->data), area);
	}

	for (l = check_relations; l; l = l->next)
		remove_change_from_evaluation_area (l->data);
	
	remove_change_from_evaluation_area (&(additional_relations.relation1));
	remove_change_from_evaluation_area (&(additional_relations.relation2));
}
Exemplo n.º 9
0
/*
 * Used by the arch code to replace the exception handling
 * with a direct branch. This is safe to do if the 
 * exception object isn't used, no rethrow statement and
 * no filter statement (verify).
 *
 */
MonoInst *
mono_branch_optimize_exception_target (MonoCompile *cfg, MonoBasicBlock *bb, const char * exname)
{
	MonoMethodHeader *header = cfg->header;
	MonoExceptionClause *clause;
	MonoClass *exclass;
	int i;

	if (!(cfg->opt & MONO_OPT_EXCEPTION))
		return NULL;

	if (bb->region == -1 || !MONO_BBLOCK_IS_IN_REGION (bb, MONO_REGION_TRY))
		return NULL;

	exclass = mono_class_from_name (mono_get_corlib (), "System", exname);
	/* search for the handler */
	for (i = 0; i < header->num_clauses; ++i) {
		clause = &header->clauses [i];
		if (MONO_OFFSET_IN_CLAUSE (clause, bb->real_offset)) {
			if (clause->flags == MONO_EXCEPTION_CLAUSE_NONE && clause->data.catch_class && mono_class_is_assignable_from (clause->data.catch_class, exclass)) {
				MonoBasicBlock *tbb;

				/* get the basic block for the handler and 
				 * check if the exception object is used.
				 * Flag is set during method_to_ir due to 
				 * pop-op is optmized away in codegen (burg).
				 */
				tbb = cfg->cil_offset_to_bb [clause->handler_offset];
				if (tbb && tbb->flags & BB_EXCEPTION_DEAD_OBJ && !(tbb->flags & BB_EXCEPTION_UNSAFE)) {
					MonoBasicBlock *targetbb = tbb;
					gboolean unsafe = FALSE;

					/* Check if this catch clause is ok to optimize by
					 * looking for the BB_EXCEPTION_UNSAFE in every BB that
					 * belongs to the same region. 
					 *
					 * UNSAFE flag is set during method_to_ir (OP_RETHROW)
					 */
					while (!unsafe && tbb->next_bb && tbb->region == tbb->next_bb->region) {
						if (tbb->next_bb->flags & BB_EXCEPTION_UNSAFE)  {
							unsafe = TRUE;
							break;
						}
						tbb = tbb->next_bb;
					}

					if (!unsafe) {
						MonoInst *jump;

						/* Create dummy inst to allow easier integration in
						 * arch dependent code (opcode ignored)
						 */
						MONO_INST_NEW (cfg, jump, OP_BR);

						/* Allocate memory for our branch target */
						jump->inst_i1 = mono_mempool_alloc0 (cfg->mempool, sizeof (MonoInst));
						jump->inst_true_bb = targetbb;

						if (cfg->verbose_level > 2) 
							g_print ("found exception to optimize - returning branch to BB%d (%s) (instead of throw) for method %s:%s\n", targetbb->block_num, clause->data.catch_class->name, cfg->method->klass->name, cfg->method->name);

						return jump;
					} 

					return NULL;
				} else {
					/* Branching to an outer clause could skip inner clauses */
					return NULL;
				}
			} else {
				/* Branching to an outer clause could skip inner clauses */
				return NULL;
			}
		}
	}

	return NULL;
}
Exemplo n.º 10
0
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_ins (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];
				/* 
				 * Have to remove non-critical edges whose source ends with a BR_REG
				 * ins too, since inserting a computation before the BR_REG could 
				 * overwrite the sreg1 of the ins.
				 */
				if ((in_bb->out_count > 1) || (in_bb->out_count == 1 && in_bb->last_ins && in_bb->last_ins->opcode == OP_BR_REG)) {
					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 (mono_bb_is_fall_through (cfg, previous_bb)) {
						if (previous_bb != cfg->bb_entry) {
							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;

							mono_unlink_bblock (cfg, previous_bb, bb);
							mono_link_bblock (cfg, new_bb_after_entry, bb);
							mono_link_bblock (cfg, previous_bb, new_bb_after_entry);
							
							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_ins (bb->last_ins);
			}
			printf ("\n");
		}
	}
}