mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-22 03:54:33 +02:00
6384206: Phis which are later unneeded are impairing our ability to inline based on static types
Reviewed-by: rasbold, jrose
This commit is contained in:
parent
eee15b163e
commit
4b1e242299
18 changed files with 1082 additions and 401 deletions
|
@ -29,17 +29,17 @@
|
|||
// the most. Some of the non-static variables are needed in bytecodeInfo.cpp
|
||||
// and eventually should be encapsulated in a proper class (gri 8/18/98).
|
||||
|
||||
int nodes_created = 0; int nodes_created_old = 0;
|
||||
int methods_parsed = 0; int methods_parsed_old = 0;
|
||||
int methods_seen = 0; int methods_seen_old = 0;
|
||||
int nodes_created = 0;
|
||||
int methods_parsed = 0;
|
||||
int methods_seen = 0;
|
||||
int blocks_parsed = 0;
|
||||
int blocks_seen = 0;
|
||||
|
||||
int explicit_null_checks_inserted = 0, explicit_null_checks_inserted_old = 0;
|
||||
int explicit_null_checks_elided = 0, explicit_null_checks_elided_old = 0;
|
||||
int explicit_null_checks_inserted = 0;
|
||||
int explicit_null_checks_elided = 0;
|
||||
int all_null_checks_found = 0, implicit_null_checks = 0;
|
||||
int implicit_null_throws = 0;
|
||||
|
||||
int parse_idx = 0;
|
||||
size_t parse_arena = 0;
|
||||
int reclaim_idx = 0;
|
||||
int reclaim_in = 0;
|
||||
int reclaim_node = 0;
|
||||
|
@ -61,6 +61,7 @@ void Parse::print_statistics() {
|
|||
tty->cr();
|
||||
if (methods_seen != methods_parsed)
|
||||
tty->print_cr("Reasons for parse failures (NOT cumulative):");
|
||||
tty->print_cr("Blocks parsed: %d Blocks seen: %d", blocks_parsed, blocks_seen);
|
||||
|
||||
if( explicit_null_checks_inserted )
|
||||
tty->print_cr("%d original NULL checks - %d elided (%2d%%); optimizer leaves %d,", explicit_null_checks_inserted, explicit_null_checks_elided, (100*explicit_null_checks_elided)/explicit_null_checks_inserted, all_null_checks_found);
|
||||
|
@ -373,6 +374,12 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses)
|
|||
C->record_method_not_compilable_all_tiers(_flow->failure_reason());
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
if (_flow->has_irreducible_entry()) {
|
||||
C->set_parsed_irreducible_loop(true);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (_expected_uses <= 0) {
|
||||
_prof_factor = 1;
|
||||
} else {
|
||||
|
@ -556,118 +563,93 @@ Parse::Parse(JVMState* caller, ciMethod* parse_method, float expected_uses)
|
|||
set_map(entry_map);
|
||||
do_exits();
|
||||
|
||||
// Collect a few more statistics.
|
||||
parse_idx += C->unique();
|
||||
parse_arena += C->node_arena()->used();
|
||||
|
||||
if (log) log->done("parse nodes='%d' memory='%d'",
|
||||
C->unique(), C->node_arena()->used());
|
||||
}
|
||||
|
||||
//---------------------------do_all_blocks-------------------------------------
|
||||
void Parse::do_all_blocks() {
|
||||
_blocks_merged = 0;
|
||||
_blocks_parsed = 0;
|
||||
bool has_irreducible = flow()->has_irreducible_entry();
|
||||
|
||||
int old_blocks_merged = -1;
|
||||
int old_blocks_parsed = -1;
|
||||
// Walk over all blocks in Reverse Post-Order.
|
||||
while (true) {
|
||||
bool progress = false;
|
||||
for (int rpo = 0; rpo < block_count(); rpo++) {
|
||||
Block* block = rpo_at(rpo);
|
||||
|
||||
for (int tries = 0; ; tries++) {
|
||||
visit_blocks();
|
||||
if (failing()) return; // Check for bailout
|
||||
if (block->is_parsed()) continue;
|
||||
|
||||
// No need for a work list. The outer loop is hardly ever repeated.
|
||||
// The following loop traverses the blocks in a reasonable pre-order,
|
||||
// as produced by the ciTypeFlow pass.
|
||||
|
||||
// This loop can be taken more than once if there are two entries to
|
||||
// a loop (irreduceable CFG), and the edge which ciTypeFlow chose
|
||||
// as the first predecessor to the loop goes dead in the parser,
|
||||
// due to parse-time optimization. (Could happen with obfuscated code.)
|
||||
|
||||
// Look for progress, or the lack of it:
|
||||
if (_blocks_parsed == block_count()) {
|
||||
// That's all, folks.
|
||||
if (TraceOptoParse) {
|
||||
tty->print_cr("All blocks parsed.");
|
||||
if (!block->is_merged()) {
|
||||
// Dead block, no state reaches this block
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
|
||||
// Prepare to parse this block.
|
||||
load_state_from(block);
|
||||
|
||||
if (stopped()) {
|
||||
// Block is dead.
|
||||
continue;
|
||||
}
|
||||
|
||||
blocks_parsed++;
|
||||
|
||||
progress = true;
|
||||
if (block->is_loop_head() || block->is_handler() || has_irreducible && !block->is_ready()) {
|
||||
// Not all preds have been parsed. We must build phis everywhere.
|
||||
// (Note that dead locals do not get phis built, ever.)
|
||||
ensure_phis_everywhere();
|
||||
|
||||
// Leave behind an undisturbed copy of the map, for future merges.
|
||||
set_map(clone_map());
|
||||
}
|
||||
|
||||
if (control()->is_Region() && !block->is_loop_head() && !has_irreducible && !block->is_handler()) {
|
||||
// In the absence of irreducible loops, the Region and Phis
|
||||
// associated with a merge that doesn't involve a backedge can
|
||||
// be simplfied now since the RPO parsing order guarantees
|
||||
// that any path which was supposed to reach here has already
|
||||
// been parsed or must be dead.
|
||||
Node* c = control();
|
||||
Node* result = _gvn.transform_no_reclaim(control());
|
||||
if (c != result && TraceOptoParse) {
|
||||
tty->print_cr("Block #%d replace %d with %d", block->rpo(), c->_idx, result->_idx);
|
||||
}
|
||||
if (result != top()) {
|
||||
record_for_igvn(result);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse the block.
|
||||
do_one_block();
|
||||
|
||||
// Check for bailouts.
|
||||
if (failing()) return;
|
||||
}
|
||||
|
||||
// How much work was done this time around?
|
||||
int new_blocks_merged = _blocks_merged - old_blocks_merged;
|
||||
int new_blocks_parsed = _blocks_parsed - old_blocks_parsed;
|
||||
if (new_blocks_merged == 0) {
|
||||
if (TraceOptoParse) {
|
||||
tty->print_cr("All live blocks parsed; %d dead blocks.", block_count() - _blocks_parsed);
|
||||
}
|
||||
// No new blocks have become parseable. Some blocks are just dead.
|
||||
// with irreducible loops multiple passes might be necessary to parse everything
|
||||
if (!has_irreducible || !progress) {
|
||||
break;
|
||||
}
|
||||
assert(new_blocks_parsed > 0, "must make progress");
|
||||
assert(tries < block_count(), "the pre-order cannot be this bad!");
|
||||
|
||||
old_blocks_merged = _blocks_merged;
|
||||
old_blocks_parsed = _blocks_parsed;
|
||||
}
|
||||
|
||||
blocks_seen += block_count();
|
||||
|
||||
#ifndef PRODUCT
|
||||
// Make sure there are no half-processed blocks remaining.
|
||||
// Every remaining unprocessed block is dead and may be ignored now.
|
||||
for (int po = 0; po < block_count(); po++) {
|
||||
Block* block = pre_order_at(po);
|
||||
for (int rpo = 0; rpo < block_count(); rpo++) {
|
||||
Block* block = rpo_at(rpo);
|
||||
if (!block->is_parsed()) {
|
||||
if (TraceOptoParse) {
|
||||
tty->print("Skipped dead block %d at bci:%d", po, block->start());
|
||||
assert(!block->is_merged(), "no half-processed blocks");
|
||||
tty->print_cr("Skipped dead block %d at bci:%d", rpo, block->start());
|
||||
}
|
||||
assert(!block->is_merged(), "no half-processed blocks");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//---------------------------visit_blocks--------------------------------------
|
||||
void Parse::visit_blocks() {
|
||||
// Walk over all blocks, parsing every one that has been reached (merged).
|
||||
for (int po = 0; po < block_count(); po++) {
|
||||
Block* block = pre_order_at(po);
|
||||
|
||||
if (block->is_parsed()) {
|
||||
// Do not parse twice.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!block->is_merged()) {
|
||||
// No state on this block. It had not yet been reached.
|
||||
// Delay reaching it until later.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Prepare to parse this block.
|
||||
load_state_from(block);
|
||||
|
||||
if (stopped()) {
|
||||
// Block is dead.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!block->is_ready() || block->is_handler()) {
|
||||
// Not all preds have been parsed. We must build phis everywhere.
|
||||
// (Note that dead locals do not get phis built, ever.)
|
||||
ensure_phis_everywhere();
|
||||
|
||||
// Leave behind an undisturbed copy of the map, for future merges.
|
||||
set_map(clone_map());
|
||||
}
|
||||
|
||||
// Ready or not, parse the block.
|
||||
do_one_block();
|
||||
|
||||
// Check for bailouts.
|
||||
if (failing()) return;
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------build_exits----------------------------------
|
||||
// Build normal and exceptional exit merge points.
|
||||
void Parse::build_exits() {
|
||||
|
@ -1134,24 +1116,24 @@ void Parse::init_blocks() {
|
|||
_blocks = NEW_RESOURCE_ARRAY(Block, _block_count);
|
||||
Copy::zero_to_bytes(_blocks, sizeof(Block)*_block_count);
|
||||
|
||||
int po;
|
||||
int rpo;
|
||||
|
||||
// Initialize the structs.
|
||||
for (po = 0; po < block_count(); po++) {
|
||||
Block* block = pre_order_at(po);
|
||||
block->init_node(this, po);
|
||||
for (rpo = 0; rpo < block_count(); rpo++) {
|
||||
Block* block = rpo_at(rpo);
|
||||
block->init_node(this, rpo);
|
||||
}
|
||||
|
||||
// Collect predecessor and successor information.
|
||||
for (po = 0; po < block_count(); po++) {
|
||||
Block* block = pre_order_at(po);
|
||||
for (rpo = 0; rpo < block_count(); rpo++) {
|
||||
Block* block = rpo_at(rpo);
|
||||
block->init_graph(this);
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------init_node-------------------------------------
|
||||
void Parse::Block::init_node(Parse* outer, int po) {
|
||||
_flow = outer->flow()->pre_order_at(po);
|
||||
void Parse::Block::init_node(Parse* outer, int rpo) {
|
||||
_flow = outer->flow()->rpo_at(rpo);
|
||||
_pred_count = 0;
|
||||
_preds_parsed = 0;
|
||||
_count = 0;
|
||||
|
@ -1177,7 +1159,7 @@ void Parse::Block::init_graph(Parse* outer) {
|
|||
int p = 0;
|
||||
for (int i = 0; i < ns+ne; i++) {
|
||||
ciTypeFlow::Block* tf2 = (i < ns) ? tfs->at(i) : tfe->at(i-ns);
|
||||
Block* block2 = outer->pre_order_at(tf2->pre_order());
|
||||
Block* block2 = outer->rpo_at(tf2->rpo());
|
||||
_successors[i] = block2;
|
||||
|
||||
// Accumulate pred info for the other block, too.
|
||||
|
@ -1368,10 +1350,11 @@ void Parse::do_one_block() {
|
|||
int nt = b->all_successors();
|
||||
|
||||
tty->print("Parsing block #%d at bci [%d,%d), successors: ",
|
||||
block()->pre_order(), block()->start(), block()->limit());
|
||||
block()->rpo(), block()->start(), block()->limit());
|
||||
for (int i = 0; i < nt; i++) {
|
||||
tty->print((( i < ns) ? " %d" : " %d(e)"), b->successor_at(i)->pre_order());
|
||||
tty->print((( i < ns) ? " %d" : " %d(e)"), b->successor_at(i)->rpo());
|
||||
}
|
||||
if (b->is_loop_head()) tty->print(" lphd");
|
||||
tty->print_cr("");
|
||||
}
|
||||
|
||||
|
@ -1501,7 +1484,7 @@ void Parse::handle_missing_successor(int target_bci) {
|
|||
#ifndef PRODUCT
|
||||
Block* b = block();
|
||||
int trap_bci = b->flow()->has_trap()? b->flow()->trap_bci(): -1;
|
||||
tty->print_cr("### Missing successor at bci:%d for block #%d (trap_bci:%d)", target_bci, b->pre_order(), trap_bci);
|
||||
tty->print_cr("### Missing successor at bci:%d for block #%d (trap_bci:%d)", target_bci, b->rpo(), trap_bci);
|
||||
#endif
|
||||
ShouldNotReachHere();
|
||||
}
|
||||
|
@ -1509,7 +1492,7 @@ void Parse::handle_missing_successor(int target_bci) {
|
|||
//--------------------------merge_common---------------------------------------
|
||||
void Parse::merge_common(Parse::Block* target, int pnum) {
|
||||
if (TraceOptoParse) {
|
||||
tty->print("Merging state at block #%d bci:%d", target->pre_order(), target->start());
|
||||
tty->print("Merging state at block #%d bci:%d", target->rpo(), target->start());
|
||||
}
|
||||
|
||||
// Zap extra stack slots to top
|
||||
|
@ -1534,6 +1517,7 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
|
|||
// which must not be allowed into this block's map.)
|
||||
if (pnum > PhiNode::Input // Known multiple inputs.
|
||||
|| target->is_handler() // These have unpredictable inputs.
|
||||
|| target->is_loop_head() // Known multiple inputs
|
||||
|| control()->is_Region()) { // We must hide this guy.
|
||||
// Add a Region to start the new basic block. Phis will be added
|
||||
// later lazily.
|
||||
|
@ -1575,15 +1559,21 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
|
|||
|
||||
// Compute where to merge into
|
||||
// Merge incoming control path
|
||||
r->set_req(pnum, newin->control());
|
||||
r->init_req(pnum, newin->control());
|
||||
|
||||
if (pnum == 1) { // Last merge for this Region?
|
||||
_gvn.transform_no_reclaim(r);
|
||||
if (!block()->flow()->is_irreducible_entry()) {
|
||||
Node* result = _gvn.transform_no_reclaim(r);
|
||||
if (r != result && TraceOptoParse) {
|
||||
tty->print_cr("Block #%d replace %d with %d", block()->rpo(), r->_idx, result->_idx);
|
||||
}
|
||||
}
|
||||
record_for_igvn(r);
|
||||
}
|
||||
|
||||
// Update all the non-control inputs to map:
|
||||
assert(TypeFunc::Parms == newin->jvms()->locoff(), "parser map should contain only youngest jvms");
|
||||
bool check_elide_phi = target->is_SEL_backedge(save_block);
|
||||
for (uint j = 1; j < newin->req(); j++) {
|
||||
Node* m = map()->in(j); // Current state of target.
|
||||
Node* n = newin->in(j); // Incoming change to target state.
|
||||
|
@ -1603,7 +1593,11 @@ void Parse::merge_common(Parse::Block* target, int pnum) {
|
|||
merge_memory_edges(n->as_MergeMem(), pnum, nophi);
|
||||
continue;
|
||||
default: // All normal stuff
|
||||
if (phi == NULL) phi = ensure_phi(j, nophi);
|
||||
if (phi == NULL) {
|
||||
if (!check_elide_phi || !target->can_elide_SEL_phi(j)) {
|
||||
phi = ensure_phi(j, nophi);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -1736,9 +1730,13 @@ void Parse::ensure_phis_everywhere() {
|
|||
uint nof_monitors = map()->jvms()->nof_monitors();
|
||||
|
||||
assert(TypeFunc::Parms == map()->jvms()->locoff(), "parser map should contain only youngest jvms");
|
||||
bool check_elide_phi = block()->is_SEL_head();
|
||||
for (uint i = TypeFunc::Parms; i < monoff; i++) {
|
||||
ensure_phi(i);
|
||||
if (!check_elide_phi || !block()->can_elide_SEL_phi(i)) {
|
||||
ensure_phi(i);
|
||||
}
|
||||
}
|
||||
|
||||
// Even monitors need Phis, though they are well-structured.
|
||||
// This is true for OSR methods, and also for the rare cases where
|
||||
// a monitor object is the subject of a replace_in_map operation.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue