ciMethodBlocks::ciMethodBlocks(Arena *arena, ciMethod *meth): _method(meth), _arena(arena), _num_blocks(0), _code_size(meth->code_size()) { int block_estimate = _code_size / 8; _blocks = new(_arena) GrowableArray<ciBlock *>(block_estimate); int b2bsize = _code_size * sizeof(ciBlock **); _bci_to_block = (ciBlock **) arena->Amalloc(b2bsize); Copy::zero_to_words((HeapWord*) _bci_to_block, b2bsize / sizeof(HeapWord)); // create initial block covering the entire method ciBlock *b = new(arena) ciBlock(_method, _num_blocks++, 0); _blocks->append(b); _bci_to_block[0] = b; // create blocks for exception handlers if (meth->has_exception_handlers()) { for(ciExceptionHandlerStream str(meth); !str.is_done(); str.next()) { ciExceptionHandler* handler = str.handler(); ciBlock *eb = make_block_at(handler->handler_bci()); // // Several exception handlers can have the same handler_bci: // // try { // if (a.foo(b) < 0) { // return a.error(); // } // return CoderResult.UNDERFLOW; // } finally { // a.position(b); // } // // The try block above is divided into 2 exception blocks // separated by 'areturn' bci. // int ex_start = handler->start(); int ex_end = handler->limit(); // ensure a block at the start of exception range and start of following code (void) make_block_at(ex_start); if (ex_end < _code_size) (void) make_block_at(ex_end); if (eb->is_handler()) { // Extend old handler exception range to cover additional range. int old_ex_start = eb->ex_start_bci(); int old_ex_end = eb->ex_limit_bci(); if (ex_start > old_ex_start) ex_start = old_ex_start; if (ex_end < old_ex_end) ex_end = old_ex_end; eb->clear_exception_handler(); // Reset exception information } eb->set_exception_range(ex_start, ex_end); } } // scan the bytecodes and identify blocks do_analysis(); // mark blocks that have exception handlers if (meth->has_exception_handlers()) { for(ciExceptionHandlerStream str(meth); !str.is_done(); str.next()) { ciExceptionHandler* handler = str.handler(); int ex_start = handler->start(); int ex_end = handler->limit(); int bci = ex_start; while (bci < ex_end) { ciBlock *b = block_containing(bci); b->set_has_handler(); bci = b->limit_bci(); } } } }
void MethodLiveness::init_basic_blocks() { bool bailout = false; int method_len = method()->code_size(); // Create an array to store the bci->BasicBlock mapping. _block_map = new (arena()) GrowableArray<BasicBlock*>(arena(), method_len, method_len, NULL); _block_list = new (arena()) GrowableArray<BasicBlock*>(arena(), 128, 0, NULL); // Used for patching up jsr/ret control flow. GrowableArray<BasicBlock*>* jsr_exit_list = new GrowableArray<BasicBlock*>(5); GrowableArray<BasicBlock*>* ret_list = new GrowableArray<BasicBlock*>(5); // Make blocks begin at all exception handling instructions. { ciExceptionHandlerStream handlers(method()); for (; !handlers.is_done(); handlers.next()) { ciExceptionHandler* handler = handlers.handler(); int handler_bci = handler->handler_bci(); make_block_at(handler_bci, NULL); } } BasicBlock *current_block = NULL; ciByteCodeStream bytes(method()); Bytecodes::Code code; while ((code = bytes.next()) != ciByteCodeStream::EOBC && !bailout) { int bci = bytes.cur_bci(); // Should we start a new block here? BasicBlock *other = _block_map->at(bci); if (other == NULL) { // This bci has not yet been marked as the start of // a new basic block. If current_block is NULL, then // we are beginning a new block. Otherwise, we continue // with the old block. if (current_block == NULL) { // Make a new block with no predecessors. current_block = make_block_at(bci, NULL); } // Mark this index as belonging to the current block. _block_map->at_put(bci, current_block); } else { // This bci has been marked as the start of a new basic // block. if (current_block != NULL) { other->add_normal_predecessor(current_block); current_block->set_limit_bci(bci); } current_block = other; } // Now we need to interpret the instruction's effect // on control flow. switch (code) { assert (current_block != NULL, "we must have a current block"); case Bytecodes::_ifeq: case Bytecodes::_ifne: case Bytecodes::_iflt: case Bytecodes::_ifge: case Bytecodes::_ifgt: case Bytecodes::_ifle: case Bytecodes::_if_icmpeq: case Bytecodes::_if_icmpne: case Bytecodes::_if_icmplt: case Bytecodes::_if_icmpge: case Bytecodes::_if_icmpgt: case Bytecodes::_if_icmple: case Bytecodes::_if_acmpeq: case Bytecodes::_if_acmpne: case Bytecodes::_ifnull: case Bytecodes::_ifnonnull: // Two way branch. Make a new block at each destination. make_block_at(bytes.next_bci(), current_block); make_block_at(bytes.get_dest(), current_block); current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; case Bytecodes::_goto: make_block_at(bytes.get_dest(), current_block); current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; case Bytecodes::_goto_w: make_block_at(bytes.get_far_dest(), current_block); current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; case Bytecodes::_tableswitch: { Bytecode_tableswitch *tableswitch = Bytecode_tableswitch_at(bytes.cur_bcp()); int len = tableswitch->length(); make_block_at(bci + tableswitch->default_offset(), current_block); while (--len >= 0) { make_block_at(bci + tableswitch->dest_offset_at(len), current_block); } current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; } // Some synthetic opcodes here case Bytecodes::_fast_linearswitch: case Bytecodes::_fast_binaryswitch: case Bytecodes::_lookupswitch: { Bytecode_lookupswitch *lookupswitch = Bytecode_lookupswitch_at(bytes.cur_bcp()); int npairs = lookupswitch->number_of_pairs(); make_block_at(bci + lookupswitch->default_offset(), current_block); while(--npairs >= 0) { LookupswitchPair *pair = lookupswitch->pair_at(npairs); make_block_at(bci + pair->offset(), current_block); } current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; } case Bytecodes::_jsr: { assert(bytes.is_wide()==false, "sanity check"); make_block_at(bytes.get_dest(), current_block); BasicBlock *jsrExit = make_block_at(bci + 3, NULL); jsr_exit_list->append(jsrExit); current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; } case Bytecodes::_jsr_w: { make_block_at(bytes.get_far_dest(), current_block); BasicBlock *jsrExit = make_block_at(bci + 5, NULL); jsr_exit_list->append(jsrExit); current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; } case Bytecodes::_wide: assert(false, "wide opcodes should not be seen here"); break; case Bytecodes::_athrow: case Bytecodes::_ireturn: case Bytecodes::_lreturn: case Bytecodes::_freturn: case Bytecodes::_dreturn: case Bytecodes::_areturn: case Bytecodes::_return: // We are done with the current block. These opcodes are // not the normal predecessors of any other opcodes. current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; case Bytecodes::_ret: // We will patch up jsr/rets in a subsequent pass. ret_list->append(current_block); current_block->set_limit_bci(bytes.next_bci()); current_block = NULL; break; case Bytecodes::_breakpoint: // Bail out of there are breakpoints in here. bailout = true; break; default: // Do nothing. break; } } if (bailout) { _block_list->clear(); _block_map = NULL; // do not use this field now! return; } // Patch up the jsr/ret's. We conservatively assume that any ret // can return to any jsr site. int ret_list_len = ret_list->length(); int jsr_exit_list_len = jsr_exit_list->length(); if (ret_list_len > 0 && jsr_exit_list_len > 0) { for (int i = jsr_exit_list_len - 1; i >= 0; i--) { BasicBlock *jsrExit = jsr_exit_list->at(i); for (int i = ret_list_len - 1; i >= 0; i--) { jsrExit->add_normal_predecessor(ret_list->at(i)); } } } // Compute exception edges. for (int b=_block_list->length()-1; b >= 0; b--) { BasicBlock *block = _block_list->at(b); int block_start = block->start_bci(); int block_limit = block->limit_bci(); ciExceptionHandlerStream handlers(method()); for (; !handlers.is_done(); handlers.next()) { ciExceptionHandler* handler = handlers.handler(); int start = handler->start(); int limit = handler->limit(); int handler_bci = handler->handler_bci(); int intersect_start = MAX2(block_start, start); int intersect_limit = MIN2(block_limit, limit); if (intersect_start < intersect_limit) { // The catch range has a nonempty intersection with this // basic block. That means this basic block can be an // exceptional predecessor. _block_map->at(handler_bci)->add_exception_predecessor(block); if (handler->is_catch_all()) { // This is a catch-all block. if (intersect_start == block_start && intersect_limit == block_limit) { // The basic block is entirely contained in this catch-all block. // Skip the rest of the exception handlers -- they can never be // reached in execution. break; } } } } } }
void ciMethodBlocks::do_analysis() { ciBytecodeStream s(_method); ciBlock *cur_block = block_containing(0); int limit_bci = _method->code_size(); while (s.next() != ciBytecodeStream::EOBC()) { int bci = s.cur_bci(); // Determine if a new block has been made at the current bci. If // this block differs from our current range, switch to the new // one and end the old one. assert(cur_block != NULL, "must always have a current block"); ciBlock *new_block = block_containing(bci); if (new_block == NULL || new_block == cur_block) { // We have not marked this bci as the start of a new block. // Keep interpreting the current_range. _bci_to_block[bci] = cur_block; } else { cur_block->set_limit_bci(bci); cur_block = new_block; } switch (s.cur_bc()) { case Bytecodes::_ifeq : case Bytecodes::_ifne : case Bytecodes::_iflt : case Bytecodes::_ifge : case Bytecodes::_ifgt : case Bytecodes::_ifle : case Bytecodes::_if_icmpeq : case Bytecodes::_if_icmpne : case Bytecodes::_if_icmplt : case Bytecodes::_if_icmpge : case Bytecodes::_if_icmpgt : case Bytecodes::_if_icmple : case Bytecodes::_if_acmpeq : case Bytecodes::_if_acmpne : case Bytecodes::_ifnull : case Bytecodes::_ifnonnull : { cur_block->set_control_bci(bci); ciBlock *fall_through = make_block_at(s.next_bci()); int dest_bci = s.get_dest(); ciBlock *dest = make_block_at(dest_bci); break; } case Bytecodes::_goto : { cur_block->set_control_bci(bci); if (s.next_bci() < limit_bci) { (void) make_block_at(s.next_bci()); } int dest_bci = s.get_dest(); ciBlock *dest = make_block_at(dest_bci); break; } case Bytecodes::_jsr : { cur_block->set_control_bci(bci); ciBlock *ret = make_block_at(s.next_bci()); int dest_bci = s.get_dest(); ciBlock *dest = make_block_at(dest_bci); break; } case Bytecodes::_tableswitch : { cur_block->set_control_bci(bci); Bytecode_tableswitch* switch_ = Bytecode_tableswitch_at(s.cur_bcp()); int len = switch_->length(); ciBlock *dest; int dest_bci; for (int i = 0; i < len; i++) { dest_bci = s.cur_bci() + switch_->dest_offset_at(i); dest = make_block_at(dest_bci); } dest_bci = s.cur_bci() + switch_->default_offset(); make_block_at(dest_bci); if (s.next_bci() < limit_bci) { dest = make_block_at(s.next_bci()); } } break; case Bytecodes::_lookupswitch: { cur_block->set_control_bci(bci); Bytecode_lookupswitch* switch_ = Bytecode_lookupswitch_at(s.cur_bcp()); int len = switch_->number_of_pairs(); ciBlock *dest; int dest_bci; for (int i = 0; i < len; i++) { dest_bci = s.cur_bci() + switch_->pair_at(i)->offset(); dest = make_block_at(dest_bci); } dest_bci = s.cur_bci() + switch_->default_offset(); dest = make_block_at(dest_bci); if (s.next_bci() < limit_bci) { dest = make_block_at(s.next_bci()); } } break; case Bytecodes::_goto_w : { cur_block->set_control_bci(bci); if (s.next_bci() < limit_bci) { (void) make_block_at(s.next_bci()); } int dest_bci = s.get_far_dest(); ciBlock *dest = make_block_at(dest_bci); break; } case Bytecodes::_jsr_w : { cur_block->set_control_bci(bci); ciBlock *ret = make_block_at(s.next_bci()); int dest_bci = s.get_far_dest(); ciBlock *dest = make_block_at(dest_bci); break; } case Bytecodes::_athrow : cur_block->set_may_throw(); // fall-through case Bytecodes::_ret : case Bytecodes::_ireturn : case Bytecodes::_lreturn : case Bytecodes::_freturn : case Bytecodes::_dreturn : case Bytecodes::_areturn : case Bytecodes::_return : cur_block->set_control_bci(bci); if (s.next_bci() < limit_bci) { (void) make_block_at(s.next_bci()); } break; } } // End the last block cur_block->set_limit_bci(limit_bci); }