mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-18 01:54:47 +02:00
8257211: C2: Enable call devirtualization during post-parse phase
Reviewed-by: kvn, neliasso, thartmann
This commit is contained in:
parent
149a02f99a
commit
62c7788b29
15 changed files with 628 additions and 265 deletions
|
@ -339,26 +339,71 @@ void Compile::remove_useless_late_inlines(GrowableArray<CallGenerator*>* inlines
|
|||
int shift = 0;
|
||||
for (int i = 0; i < inlines->length(); i++) {
|
||||
CallGenerator* cg = inlines->at(i);
|
||||
CallNode* call = cg->call_node();
|
||||
if (shift > 0) {
|
||||
inlines->at_put(i-shift, cg);
|
||||
}
|
||||
if (!useful.member(call)) {
|
||||
shift++;
|
||||
if (useful.member(cg->call_node())) {
|
||||
if (shift > 0) {
|
||||
inlines->at_put(i - shift, cg);
|
||||
}
|
||||
} else {
|
||||
shift++; // skip over the dead element
|
||||
}
|
||||
}
|
||||
inlines->trunc_to(inlines->length()-shift);
|
||||
if (shift > 0) {
|
||||
inlines->trunc_to(inlines->length() - shift); // remove last elements from compacted array
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::remove_useless_late_inlines(GrowableArray<CallGenerator*>* inlines, Node* dead) {
|
||||
assert(dead != NULL && dead->is_Call(), "sanity");
|
||||
int found = 0;
|
||||
for (int i = 0; i < inlines->length(); i++) {
|
||||
if (inlines->at(i)->call_node() == dead) {
|
||||
inlines->remove_at(i);
|
||||
found++;
|
||||
NOT_DEBUG( break; ) // elements are unique, so exit early
|
||||
}
|
||||
}
|
||||
assert(found <= 1, "not unique");
|
||||
}
|
||||
|
||||
void Compile::remove_useless_nodes(GrowableArray<Node*>& node_list, Unique_Node_List& useful) {
|
||||
for (int i = node_list.length() - 1; i >= 0; i--) {
|
||||
Node* n = node_list.at(i);
|
||||
if (!useful.member(n)) {
|
||||
node_list.remove_if_existing(n);
|
||||
node_list.delete_at(i); // replaces i-th with last element which is known to be useful (already processed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Compile::remove_useless_node(Node* dead) {
|
||||
remove_modified_node(dead);
|
||||
|
||||
// Constant node that has no out-edges and has only one in-edge from
|
||||
// root is usually dead. However, sometimes reshaping walk makes
|
||||
// it reachable by adding use edges. So, we will NOT count Con nodes
|
||||
// as dead to be conservative about the dead node count at any
|
||||
// given time.
|
||||
if (!dead->is_Con()) {
|
||||
record_dead_node(dead->_idx);
|
||||
}
|
||||
if (dead->is_macro()) {
|
||||
remove_macro_node(dead);
|
||||
}
|
||||
if (dead->is_expensive()) {
|
||||
remove_expensive_node(dead);
|
||||
}
|
||||
if (dead->for_post_loop_opts_igvn()) {
|
||||
remove_from_post_loop_opts_igvn(dead);
|
||||
}
|
||||
if (dead->is_Call()) {
|
||||
remove_useless_late_inlines( &_late_inlines, dead);
|
||||
remove_useless_late_inlines( &_string_late_inlines, dead);
|
||||
remove_useless_late_inlines( &_boxing_late_inlines, dead);
|
||||
remove_useless_late_inlines(&_vector_reboxing_late_inlines, dead);
|
||||
}
|
||||
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
|
||||
bs->unregister_potential_barrier_node(dead);
|
||||
}
|
||||
|
||||
// Disconnect all useless nodes by disconnecting those at the boundary.
|
||||
void Compile::remove_useless_nodes(Unique_Node_List &useful) {
|
||||
uint next = 0;
|
||||
|
@ -394,9 +439,9 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
|
|||
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
|
||||
bs->eliminate_useless_gc_barriers(useful, this);
|
||||
// clean up the late inline lists
|
||||
remove_useless_late_inlines(&_string_late_inlines, useful);
|
||||
remove_useless_late_inlines(&_boxing_late_inlines, useful);
|
||||
remove_useless_late_inlines(&_late_inlines, useful);
|
||||
remove_useless_late_inlines( &_late_inlines, useful);
|
||||
remove_useless_late_inlines( &_string_late_inlines, useful);
|
||||
remove_useless_late_inlines( &_boxing_late_inlines, useful);
|
||||
remove_useless_late_inlines(&_vector_reboxing_late_inlines, useful);
|
||||
debug_only(verify_graph_edges(true/*check for no_dead_code*/);)
|
||||
}
|
||||
|
@ -1860,21 +1905,34 @@ bool Compile::inline_incrementally_one() {
|
|||
assert(IncrementalInline, "incremental inlining should be on");
|
||||
|
||||
TracePhase tp("incrementalInline_inline", &timers[_t_incrInline_inline]);
|
||||
|
||||
set_inlining_progress(false);
|
||||
set_do_cleanup(false);
|
||||
int i = 0;
|
||||
for (; i <_late_inlines.length() && !inlining_progress(); i++) {
|
||||
CallGenerator* cg = _late_inlines.at(i);
|
||||
|
||||
for (int i = 0; i < _late_inlines.length(); i++) {
|
||||
_late_inlines_pos = i+1;
|
||||
cg->do_late_inline();
|
||||
if (failing()) return false;
|
||||
CallGenerator* cg = _late_inlines.at(i);
|
||||
bool does_dispatch = cg->is_virtual_late_inline() || cg->is_mh_late_inline();
|
||||
if (inlining_incrementally() || does_dispatch) { // a call can be either inlined or strength-reduced to a direct call
|
||||
cg->do_late_inline();
|
||||
assert(_late_inlines.at(i) == cg, "no insertions before current position allowed");
|
||||
if (failing()) {
|
||||
return false;
|
||||
} else if (inlining_progress()) {
|
||||
_late_inlines_pos = i+1; // restore the position in case new elements were inserted
|
||||
print_method(PHASE_INCREMENTAL_INLINE_STEP, cg->call_node(), 3);
|
||||
break; // process one call site at a time
|
||||
}
|
||||
} else {
|
||||
// Ignore late inline direct calls when inlining is not allowed.
|
||||
// They are left in the late inline list when node budget is exhausted until the list is fully drained.
|
||||
}
|
||||
}
|
||||
int j = 0;
|
||||
for (; i < _late_inlines.length(); i++, j++) {
|
||||
_late_inlines.at_put(j, _late_inlines.at(i));
|
||||
}
|
||||
_late_inlines.trunc_to(j);
|
||||
assert(inlining_progress() || _late_inlines.length() == 0, "");
|
||||
// Remove processed elements.
|
||||
_late_inlines.remove_till(_late_inlines_pos);
|
||||
_late_inlines_pos = 0;
|
||||
|
||||
assert(inlining_progress() || _late_inlines.length() == 0, "no progress");
|
||||
|
||||
bool needs_cleanup = do_cleanup() || over_inlining_cutoff();
|
||||
|
||||
|
@ -1896,6 +1954,7 @@ void Compile::inline_incrementally_cleanup(PhaseIterGVN& igvn) {
|
|||
igvn = PhaseIterGVN(initial_gvn());
|
||||
igvn.optimize();
|
||||
}
|
||||
print_method(PHASE_INCREMENTAL_INLINE_CLEANUP, 3);
|
||||
}
|
||||
|
||||
// Perform incremental inlining until bound on number of live nodes is reached
|
||||
|
@ -1919,6 +1978,18 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) {
|
|||
}
|
||||
|
||||
if (live_nodes() > (uint)LiveNodeCountInliningCutoff) {
|
||||
bool do_print_inlining = print_inlining() || print_intrinsics();
|
||||
if (do_print_inlining || log() != NULL) {
|
||||
// Print inlining message for candidates that we couldn't inline for lack of space.
|
||||
for (int i = 0; i < _late_inlines.length(); i++) {
|
||||
CallGenerator* cg = _late_inlines.at(i);
|
||||
const char* msg = "live nodes > LiveNodeCountInliningCutoff";
|
||||
if (do_print_inlining) {
|
||||
cg->print_inlining_late(msg);
|
||||
}
|
||||
log_late_inline_failure(cg, msg);
|
||||
}
|
||||
}
|
||||
break; // finish
|
||||
}
|
||||
}
|
||||
|
@ -1929,7 +2000,6 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) {
|
|||
while (inline_incrementally_one()) {
|
||||
assert(!failing(), "inconsistent");
|
||||
}
|
||||
|
||||
if (failing()) return;
|
||||
|
||||
inline_incrementally_cleanup(igvn);
|
||||
|
@ -1937,6 +2007,10 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) {
|
|||
print_method(PHASE_INCREMENTAL_INLINE_STEP, 3);
|
||||
|
||||
if (failing()) return;
|
||||
|
||||
if (_late_inlines.length() == 0) {
|
||||
break; // no more progress
|
||||
}
|
||||
}
|
||||
assert( igvn._worklist.size() == 0, "should be done with igvn" );
|
||||
|
||||
|
@ -1955,6 +2029,27 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) {
|
|||
set_inlining_incrementally(false);
|
||||
}
|
||||
|
||||
void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) {
|
||||
// "inlining_incrementally() == false" is used to signal that no inlining is allowed
|
||||
// (see LateInlineVirtualCallGenerator::do_late_inline_check() for details).
|
||||
// Tracking and verification of modified nodes is disabled by setting "_modified_nodes == NULL"
|
||||
// as if "inlining_incrementally() == true" were set.
|
||||
assert(inlining_incrementally() == false, "not allowed");
|
||||
assert(_modified_nodes == NULL, "not allowed");
|
||||
assert(_late_inlines.length() > 0, "sanity");
|
||||
|
||||
while (_late_inlines.length() > 0) {
|
||||
for_igvn()->clear();
|
||||
initial_gvn()->replace_with(&igvn);
|
||||
|
||||
while (inline_incrementally_one()) {
|
||||
assert(!failing(), "inconsistent");
|
||||
}
|
||||
if (failing()) return;
|
||||
|
||||
inline_incrementally_cleanup(igvn);
|
||||
}
|
||||
}
|
||||
|
||||
bool Compile::optimize_loops(PhaseIterGVN& igvn, LoopOptsMode mode) {
|
||||
if (_loop_opts_cnt > 0) {
|
||||
|
@ -2235,10 +2330,20 @@ void Compile::Optimize() {
|
|||
}
|
||||
|
||||
DEBUG_ONLY( _modified_nodes = NULL; )
|
||||
|
||||
assert(igvn._worklist.size() == 0, "not empty");
|
||||
|
||||
assert(_late_inlines.length() == 0 || IncrementalInlineMH || IncrementalInlineVirtual, "not empty");
|
||||
|
||||
if (_late_inlines.length() > 0) {
|
||||
// More opportunities to optimize virtual and MH calls.
|
||||
// Though it's maybe too late to perform inlining, strength-reducing them to direct calls is still an option.
|
||||
process_late_inline_calls_no_inline(igvn);
|
||||
}
|
||||
} // (End scope of igvn; run destructor if necessary for asserts.)
|
||||
|
||||
process_print_inlining();
|
||||
|
||||
// A method with only infinite loops has no edges entering loops from root
|
||||
{
|
||||
TracePhase tp("graphReshape", &timers[_t_graphReshaping]);
|
||||
|
@ -2254,8 +2359,6 @@ void Compile::Optimize() {
|
|||
|
||||
void Compile::inline_vector_reboxing_calls() {
|
||||
if (C->_vector_reboxing_late_inlines.length() > 0) {
|
||||
PhaseGVN* gvn = C->initial_gvn();
|
||||
|
||||
_late_inlines_pos = C->_late_inlines.length();
|
||||
while (_vector_reboxing_late_inlines.length() > 0) {
|
||||
CallGenerator* cg = _vector_reboxing_late_inlines.pop();
|
||||
|
@ -3261,25 +3364,17 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
|
|||
}
|
||||
|
||||
case Op_Proj: {
|
||||
if (OptimizeStringConcat) {
|
||||
ProjNode* p = n->as_Proj();
|
||||
if (p->_is_io_use) {
|
||||
if (OptimizeStringConcat || IncrementalInline) {
|
||||
ProjNode* proj = n->as_Proj();
|
||||
if (proj->_is_io_use) {
|
||||
assert(proj->_con == TypeFunc::I_O || proj->_con == TypeFunc::Memory, "");
|
||||
// Separate projections were used for the exception path which
|
||||
// are normally removed by a late inline. If it wasn't inlined
|
||||
// then they will hang around and should just be replaced with
|
||||
// the original one.
|
||||
Node* proj = NULL;
|
||||
// Replace with just one
|
||||
for (SimpleDUIterator i(p->in(0)); i.has_next(); i.next()) {
|
||||
Node *use = i.get();
|
||||
if (use->is_Proj() && p != use && use->as_Proj()->_con == p->_con) {
|
||||
proj = use;
|
||||
break;
|
||||
}
|
||||
}
|
||||
assert(proj != NULL || p->_con == TypeFunc::I_O, "io may be dropped at an infinite loop");
|
||||
if (proj != NULL) {
|
||||
p->subsume_by(proj, this);
|
||||
// the original one. Merge them.
|
||||
Node* non_io_proj = proj->in(0)->as_Multi()->proj_out_or_null(proj->_con, false /*is_io_use*/);
|
||||
if (non_io_proj != NULL) {
|
||||
proj->subsume_by(non_io_proj , this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4141,12 +4236,7 @@ Compile::PrintInliningBuffer& Compile::print_inlining_current() {
|
|||
|
||||
void Compile::print_inlining_update(CallGenerator* cg) {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
if (!cg->is_late_inline()) {
|
||||
if (print_inlining_current().cg() != NULL) {
|
||||
print_inlining_push();
|
||||
}
|
||||
print_inlining_commit();
|
||||
} else {
|
||||
if (cg->is_late_inline()) {
|
||||
if (print_inlining_current().cg() != cg &&
|
||||
(print_inlining_current().cg() != NULL ||
|
||||
print_inlining_current().ss()->size() != 0)) {
|
||||
|
@ -4154,6 +4244,11 @@ void Compile::print_inlining_update(CallGenerator* cg) {
|
|||
}
|
||||
print_inlining_commit();
|
||||
print_inlining_current().set_cg(cg);
|
||||
} else {
|
||||
if (print_inlining_current().cg() != NULL) {
|
||||
print_inlining_push();
|
||||
}
|
||||
print_inlining_commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -4161,7 +4256,7 @@ void Compile::print_inlining_update(CallGenerator* cg) {
|
|||
void Compile::print_inlining_move_to(CallGenerator* cg) {
|
||||
// We resume inlining at a late inlining call site. Locate the
|
||||
// corresponding inlining buffer so that we can update it.
|
||||
if (print_inlining()) {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
for (int i = 0; i < _print_inlining_list->length(); i++) {
|
||||
if (_print_inlining_list->adr_at(i)->cg() == cg) {
|
||||
_print_inlining_idx = i;
|
||||
|
@ -4173,7 +4268,7 @@ void Compile::print_inlining_move_to(CallGenerator* cg) {
|
|||
}
|
||||
|
||||
void Compile::print_inlining_update_delayed(CallGenerator* cg) {
|
||||
if (print_inlining()) {
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
assert(_print_inlining_stream->size() > 0, "missing inlining msg");
|
||||
assert(print_inlining_current().cg() == cg, "wrong entry");
|
||||
// replace message with new message
|
||||
|
@ -4188,22 +4283,8 @@ void Compile::print_inlining_assert_ready() {
|
|||
}
|
||||
|
||||
void Compile::process_print_inlining() {
|
||||
bool do_print_inlining = print_inlining() || print_intrinsics();
|
||||
if (do_print_inlining || log() != NULL) {
|
||||
// Print inlining message for candidates that we couldn't inline
|
||||
// for lack of space
|
||||
for (int i = 0; i < _late_inlines.length(); i++) {
|
||||
CallGenerator* cg = _late_inlines.at(i);
|
||||
if (!cg->is_mh_late_inline()) {
|
||||
const char* msg = "live nodes > LiveNodeCountInliningCutoff";
|
||||
if (do_print_inlining) {
|
||||
cg->print_inlining_late(msg);
|
||||
}
|
||||
log_late_inline_failure(cg, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (do_print_inlining) {
|
||||
assert(_late_inlines.length() == 0, "not drained yet");
|
||||
if (print_inlining() || print_intrinsics()) {
|
||||
ResourceMark rm;
|
||||
stringStream ss;
|
||||
assert(_print_inlining_list != NULL, "process_print_inlining should be called only once.");
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue