8268347: C2: nested locks optimization may create unbalanced monitor enter/exit code

Reviewed-by: roland, vlivanov, dcubed
This commit is contained in:
Vladimir Kozlov 2021-06-14 23:41:50 +00:00
parent 4a6da99f28
commit 4d8b5c70df
11 changed files with 369 additions and 39 deletions

View file

@ -432,6 +432,7 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
remove_useless_nodes(_skeleton_predicate_opaqs, useful);
remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes
remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass
remove_useless_coarsened_locks(useful); // remove useless coarsened locks nodes
BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
bs->eliminate_useless_gc_barriers(useful, this);
@ -501,6 +502,12 @@ void Compile::print_compile_messages() {
tty->print_cr("** Bailout: Recompile without boxing elimination **");
tty->print_cr("*********************************************************");
}
if ((_do_locks_coarsening != EliminateLocks) && PrintOpto) {
// Recompiling without locks coarsening
tty->print_cr("*********************************************************");
tty->print_cr("** Bailout: Recompile without locks coarsening **");
tty->print_cr("*********************************************************");
}
if (env()->break_at_compile()) {
// Open the debugger when compiling this method.
tty->print("### Breaking when compiling: ");
@ -528,13 +535,15 @@ debug_only( int Compile::_debug_idx = 100000; )
Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
bool subsume_loads, bool do_escape_analysis, bool eliminate_boxing, bool install_code, DirectiveSet* directive)
bool subsume_loads, bool do_escape_analysis, bool eliminate_boxing,
bool do_locks_coarsening, bool install_code, DirectiveSet* directive)
: Phase(Compiler),
_compile_id(ci_env->compile_id()),
_subsume_loads(subsume_loads),
_do_escape_analysis(do_escape_analysis),
_install_code(install_code),
_eliminate_boxing(eliminate_boxing),
_do_locks_coarsening(do_locks_coarsening),
_method(target),
_entry_bci(osr_bci),
_stub_function(NULL),
@ -566,6 +575,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci,
_skeleton_predicate_opaqs (comp_arena(), 8, 0, NULL),
_expensive_nodes (comp_arena(), 8, 0, NULL),
_for_post_loop_igvn(comp_arena(), 8, 0, NULL),
_coarsened_locks (comp_arena(), 8, 0, NULL),
_congraph(NULL),
NOT_PRODUCT(_printer(NULL) COMMA)
_dead_node_list(comp_arena()),
@ -832,6 +842,7 @@ Compile::Compile( ciEnv* ci_env,
_do_escape_analysis(false),
_install_code(true),
_eliminate_boxing(false),
_do_locks_coarsening(false),
_method(NULL),
_entry_bci(InvocationEntryBci),
_stub_function(stub_function),
@ -4447,6 +4458,101 @@ void Compile::add_expensive_node(Node * n) {
}
}
/**
* Track coarsened Lock and Unlock nodes.
*/
class Lock_List : public Node_List {
uint _origin_cnt;
public:
Lock_List(Arena *a, uint cnt) : Node_List(a), _origin_cnt(cnt) {}
uint origin_cnt() const { return _origin_cnt; }
};
void Compile::add_coarsened_locks(GrowableArray<AbstractLockNode*>& locks) {
int length = locks.length();
if (length > 0) {
// Have to keep this list until locks elimination during Macro nodes elimination.
Lock_List* locks_list = new (comp_arena()) Lock_List(comp_arena(), length);
for (int i = 0; i < length; i++) {
AbstractLockNode* lock = locks.at(i);
assert(lock->is_coarsened(), "expecting only coarsened AbstractLock nodes, but got '%s'[%d] node", lock->Name(), lock->_idx);
locks_list->push(lock);
}
_coarsened_locks.append(locks_list);
}
}
void Compile::remove_useless_coarsened_locks(Unique_Node_List& useful) {
int count = coarsened_count();
for (int i = 0; i < count; i++) {
Node_List* locks_list = _coarsened_locks.at(i);
for (uint j = 0; j < locks_list->size(); j++) {
Node* lock = locks_list->at(j);
assert(lock->is_AbstractLock(), "sanity");
if (!useful.member(lock)) {
locks_list->yank(lock);
}
}
}
}
void Compile::remove_coarsened_lock(Node* n) {
if (n->is_AbstractLock()) {
int count = coarsened_count();
for (int i = 0; i < count; i++) {
Node_List* locks_list = _coarsened_locks.at(i);
locks_list->yank(n);
}
}
}
bool Compile::coarsened_locks_consistent() {
int count = coarsened_count();
for (int i = 0; i < count; i++) {
bool unbalanced = false;
bool modified = false; // track locks kind modifications
Lock_List* locks_list = (Lock_List*)_coarsened_locks.at(i);
uint size = locks_list->size();
if (size != locks_list->origin_cnt()) {
unbalanced = true; // Some locks were removed from list
} else {
for (uint j = 0; j < size; j++) {
Node* lock = locks_list->at(j);
// All nodes in group should have the same state (modified or not)
if (!lock->as_AbstractLock()->is_coarsened()) {
if (j == 0) {
// first on list was modified, the rest should be too for consistency
modified = true;
} else if (!modified) {
// this lock was modified but previous locks on the list were not
unbalanced = true;
break;
}
} else if (modified) {
// previous locks on list were modified but not this lock
unbalanced = true;
break;
}
}
}
if (unbalanced) {
// unbalanced monitor enter/exit - only some [un]lock nodes were removed or modified
#ifdef ASSERT
if (PrintEliminateLocks) {
tty->print_cr("=== unbalanced coarsened locks ===");
for (uint l = 0; l < size; l++) {
locks_list->at(l)->dump();
}
}
#endif
record_failure(C2Compiler::retry_no_locks_coarsening());
return false;
}
}
return true;
}
/**
* Remove the speculative part of types and clean up the graph
*/