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; }
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; }
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); } }
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. }
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); }
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; }
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; } } }
/* * 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)); }
/* * 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; }
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"); } } }