/* * Split the basic blocks from @first at @target. * @hint is a guess of a very close to the target basic block. It is probed before the RB tree as it's often possible * to provide a near to exact guess (next block splits, switch branch targets, etc) * */ static MonoSimpleBasicBlock* bb_split (MonoSimpleBasicBlock *first, MonoSimpleBasicBlock *hint, MonoSimpleBasicBlock **root, guint target, gboolean link_blocks, MonoMethod *method, MonoError *error) { MonoSimpleBasicBlock *res, *bb = first; if (bb_idx_is_contained (hint, target)) { first = hint; } else if (hint->next && bb_idx_is_contained (hint->next, target)) { first = hint->next; } else { first = *root; do { if (bb_idx_is_contained (first, target)) break; if (first->start > target) first = first->left; else first = first->right; } while (first); } if (first == NULL) { mono_error_set_not_verifiable (error, method, "Invalid instruction target %x", target); return NULL; } if (first->start == target) return first; res = g_new0 (MonoSimpleBasicBlock, 1); res->start = target; res->end = first->end; res->next = first->next; res->out_bb = first->out_bb; res->dead = TRUE; first->end = res->start; first->next = res; first->out_bb = NULL; if (link_blocks) bb_link (first, res); bb_insert (bb, res, root); return res; }
/* * mono_basic_block_split: * * Return the list of basic blocks of method. Return NULL on failure and set @error. */ MonoSimpleBasicBlock* mono_basic_block_split (MonoMethod *method, MonoError *error) { MonoSimpleBasicBlock *bb, *root; const unsigned char *start, *end; MonoMethodHeader *header = mono_method_get_header (method); mono_error_init (error); if (!header) { mono_error_set_not_verifiable (error, method, "Could not decode header"); return NULL; } start = header->code; end = start + header->code_size; bb = g_new0 (MonoSimpleBasicBlock, 1); bb->start = 0; bb->end = end - start; bb->colour = BLACK; bb->dead = FALSE; root = bb; bb_formation_il_pass (start, end, bb, &root, method, error); if (!mono_error_ok (error)) goto fail; bb_formation_eh_pass (header, bb, &root, method, error); if (!mono_error_ok (error)) goto fail; bb_liveness (bb); #if DEBUG_BB dump_bb_list (bb, &root, g_strdup_printf("AFTER LIVENESS %s", mono_method_full_name (method, TRUE))); #endif mono_metadata_free_mh (header); return bb; fail: mono_metadata_free_mh (header); mono_basic_block_free (bb); return NULL; }
static void bb_formation_il_pass (const unsigned char *start, const unsigned char *end, MonoSimpleBasicBlock *bb, MonoSimpleBasicBlock **root, MonoMethod *method, MonoError *error) { unsigned const char *ip = start; int value, size; guint cli_addr, offset; MonoSimpleBasicBlock *branch, *next, *current; const MonoOpcode *opcode; current = bb; while (ip < end) { cli_addr = ip - start; size = mono_opcode_value_and_size (&ip, end, &value); if (size < 0) { mono_error_set_not_verifiable (error, method, "Invalid instruction %x", *ip); return; } while (current && cli_addr >= current->end) current = current->next; g_assert (current); opcode = &mono_opcodes [value]; switch (opcode->argument) { case MonoInlineNone: ip++; if (!mono_opcode_has_static_branch (value) || ip >= end) break; if (!(next = bb_split (bb, current, root, ip - start, FALSE, method, error))) return; bb_unlink (current, next); current = next; break; case MonoInlineString: case MonoInlineType: case MonoInlineField: case MonoInlineTok: case MonoInlineSig: case MonoShortInlineR: case MonoInlineI: ip += 5; break; case MonoInlineMethod: ip += 5; if (value != MONO_CEE_JMP || ip >= end) break; if (!(next = bb_split (bb, current, root, ip - start, FALSE, method, error))) return; bb_unlink (current, next); current = next; break; case MonoInlineVar: ip += 3; break; case MonoShortInlineVar: case MonoShortInlineI: ip += 2; break; case MonoInlineR: case MonoInlineI8: ip += 9; break; case MonoShortInlineBrTarget: case MonoInlineBrTarget: if (opcode->argument == MonoShortInlineBrTarget) { offset = cli_addr + 2 + (signed char)ip [1]; ip += 2; } else { offset = cli_addr + 5 + (gint32)read32 (ip + 1); ip += 5; } branch = bb_split (bb, current, root, offset, TRUE, method, error); if (!branch) return; /*If we splitted the current BB*/ if (offset < cli_addr && branch->start > current->start) current = branch; if (ip < end) { next = bb_split (bb, current, root, ip - start, opcode->flow_type != MONO_FLOW_BRANCH, method, error); if (!next) return; } else { next = NULL; } bb_link (current, branch); if (next && opcode->flow_type == MONO_FLOW_BRANCH && next != branch) { bb_unlink (current, next); current = next; } break; case MonoInlineSwitch: { MonoSimpleBasicBlock *tmp; guint32 j, n = read32 (ip + 1); ip += 5; offset = cli_addr + 5 + 4 * n; if (!(next = bb_split (bb, current, root, offset, TRUE, method, error))) return; bb_link (current, next); tmp = next; for (j = 0; j < n; ++j) { if (ip >= end) { mono_error_set_not_verifiable (error, method, "Invalid switch instruction %x", cli_addr); return; } if (!(next = bb_split (bb, next, root, offset + (gint32)read32 (ip), TRUE, method, error))) return; bb_link (current, next); ip += 4; } current = tmp; break; } default: mono_error_set_not_verifiable (error, method, "Invalid instruction %x", *ip); return; } } if (ip != end) mono_error_set_not_verifiable (error, method, "Invalid last instruction"); }