mirror of
https://github.com/openjdk/jdk.git
synced 2025-09-21 03:24:38 +02:00
6667610: (Escape Analysis) retry compilation without EA if it fails
During split unique types EA could exceed nodes limit and fail the method compilation. Reviewed-by: rasbold
This commit is contained in:
parent
17458c9660
commit
bf7f5e1887
7 changed files with 57 additions and 11 deletions
|
@ -410,6 +410,7 @@ domgraph.cpp vectset.hpp
|
||||||
|
|
||||||
escape.cpp allocation.hpp
|
escape.cpp allocation.hpp
|
||||||
escape.cpp bcEscapeAnalyzer.hpp
|
escape.cpp bcEscapeAnalyzer.hpp
|
||||||
|
escape.cpp c2compiler.hpp
|
||||||
escape.cpp callnode.hpp
|
escape.cpp callnode.hpp
|
||||||
escape.cpp cfgnode.hpp
|
escape.cpp cfgnode.hpp
|
||||||
escape.cpp compile.hpp
|
escape.cpp compile.hpp
|
||||||
|
|
|
@ -35,6 +35,9 @@ extern const int register_save_type[];
|
||||||
const char* C2Compiler::retry_no_subsuming_loads() {
|
const char* C2Compiler::retry_no_subsuming_loads() {
|
||||||
return "retry without subsuming loads";
|
return "retry without subsuming loads";
|
||||||
}
|
}
|
||||||
|
const char* C2Compiler::retry_no_escape_analysis() {
|
||||||
|
return "retry without escape analysis";
|
||||||
|
}
|
||||||
void C2Compiler::initialize_runtime() {
|
void C2Compiler::initialize_runtime() {
|
||||||
|
|
||||||
// Check assumptions used while running ADLC
|
// Check assumptions used while running ADLC
|
||||||
|
@ -101,17 +104,23 @@ void C2Compiler::compile_method(ciEnv* env,
|
||||||
initialize();
|
initialize();
|
||||||
}
|
}
|
||||||
bool subsume_loads = true;
|
bool subsume_loads = true;
|
||||||
|
bool do_escape_analysis = DoEscapeAnalysis;
|
||||||
while (!env->failing()) {
|
while (!env->failing()) {
|
||||||
// Attempt to compile while subsuming loads into machine instructions.
|
// Attempt to compile while subsuming loads into machine instructions.
|
||||||
Compile C(env, this, target, entry_bci, subsume_loads);
|
Compile C(env, this, target, entry_bci, subsume_loads, do_escape_analysis);
|
||||||
|
|
||||||
// Check result and retry if appropriate.
|
// Check result and retry if appropriate.
|
||||||
if (C.failure_reason() != NULL) {
|
if (C.failure_reason() != NULL) {
|
||||||
if (C.failure_reason_is(retry_no_subsuming_loads())) {
|
if (C.failure_reason_is(retry_no_subsuming_loads())) {
|
||||||
assert(subsume_loads, "must make progress");
|
assert(subsume_loads, "must make progress");
|
||||||
subsume_loads = false;
|
subsume_loads = false;
|
||||||
continue; // retry
|
continue; // retry
|
||||||
}
|
}
|
||||||
|
if (C.failure_reason_is(retry_no_escape_analysis())) {
|
||||||
|
assert(do_escape_analysis, "must make progress");
|
||||||
|
do_escape_analysis = false;
|
||||||
|
continue; // retry
|
||||||
|
}
|
||||||
// Pass any other failure reason up to the ciEnv.
|
// Pass any other failure reason up to the ciEnv.
|
||||||
// Note that serious, irreversible failures are already logged
|
// Note that serious, irreversible failures are already logged
|
||||||
// on the ciEnv via env->record_method_not_compilable().
|
// on the ciEnv via env->record_method_not_compilable().
|
||||||
|
|
|
@ -50,6 +50,7 @@ public:
|
||||||
|
|
||||||
// sentinel value used to trigger backtracking in compile_method().
|
// sentinel value used to trigger backtracking in compile_method().
|
||||||
static const char* retry_no_subsuming_loads();
|
static const char* retry_no_subsuming_loads();
|
||||||
|
static const char* retry_no_escape_analysis();
|
||||||
|
|
||||||
// Print compilation timers and statistics
|
// Print compilation timers and statistics
|
||||||
void print_timers();
|
void print_timers();
|
||||||
|
|
|
@ -333,6 +333,12 @@ void Compile::print_compile_messages() {
|
||||||
tty->print_cr("** Bailout: Recompile without subsuming loads **");
|
tty->print_cr("** Bailout: Recompile without subsuming loads **");
|
||||||
tty->print_cr("*********************************************************");
|
tty->print_cr("*********************************************************");
|
||||||
}
|
}
|
||||||
|
if (_do_escape_analysis != DoEscapeAnalysis && PrintOpto) {
|
||||||
|
// Recompiling without escape analysis
|
||||||
|
tty->print_cr("*********************************************************");
|
||||||
|
tty->print_cr("** Bailout: Recompile without escape analysis **");
|
||||||
|
tty->print_cr("*********************************************************");
|
||||||
|
}
|
||||||
if (env()->break_at_compile()) {
|
if (env()->break_at_compile()) {
|
||||||
// Open the debugger when compiing this method.
|
// Open the debugger when compiing this method.
|
||||||
tty->print("### Breaking when compiling: ");
|
tty->print("### Breaking when compiling: ");
|
||||||
|
@ -415,7 +421,7 @@ debug_only( int Compile::_debug_idx = 100000; )
|
||||||
// the continuation bci for on stack replacement.
|
// the continuation bci for on stack replacement.
|
||||||
|
|
||||||
|
|
||||||
Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr_bci, bool subsume_loads )
|
Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr_bci, bool subsume_loads, bool do_escape_analysis )
|
||||||
: Phase(Compiler),
|
: Phase(Compiler),
|
||||||
_env(ci_env),
|
_env(ci_env),
|
||||||
_log(ci_env->log()),
|
_log(ci_env->log()),
|
||||||
|
@ -430,6 +436,7 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||||
_for_igvn(NULL),
|
_for_igvn(NULL),
|
||||||
_warm_calls(NULL),
|
_warm_calls(NULL),
|
||||||
_subsume_loads(subsume_loads),
|
_subsume_loads(subsume_loads),
|
||||||
|
_do_escape_analysis(do_escape_analysis),
|
||||||
_failure_reason(NULL),
|
_failure_reason(NULL),
|
||||||
_code_buffer("Compile::Fill_buffer"),
|
_code_buffer("Compile::Fill_buffer"),
|
||||||
_orig_pc_slot(0),
|
_orig_pc_slot(0),
|
||||||
|
@ -487,7 +494,7 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||||
PhaseGVN gvn(node_arena(), estimated_size);
|
PhaseGVN gvn(node_arena(), estimated_size);
|
||||||
set_initial_gvn(&gvn);
|
set_initial_gvn(&gvn);
|
||||||
|
|
||||||
if (DoEscapeAnalysis)
|
if (_do_escape_analysis)
|
||||||
_congraph = new ConnectionGraph(this);
|
_congraph = new ConnectionGraph(this);
|
||||||
|
|
||||||
{ // Scope for timing the parser
|
{ // Scope for timing the parser
|
||||||
|
@ -577,6 +584,8 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||||
if (_congraph != NULL) {
|
if (_congraph != NULL) {
|
||||||
NOT_PRODUCT( TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, TimeCompiler); )
|
NOT_PRODUCT( TracePhase t2("escapeAnalysis", &_t_escapeAnalysis, TimeCompiler); )
|
||||||
_congraph->compute_escape();
|
_congraph->compute_escape();
|
||||||
|
if (failing()) return;
|
||||||
|
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
if (PrintEscapeAnalysis) {
|
if (PrintEscapeAnalysis) {
|
||||||
_congraph->dump();
|
_congraph->dump();
|
||||||
|
@ -675,6 +684,7 @@ Compile::Compile( ciEnv* ci_env,
|
||||||
_orig_pc_slot(0),
|
_orig_pc_slot(0),
|
||||||
_orig_pc_slot_offset_in_bytes(0),
|
_orig_pc_slot_offset_in_bytes(0),
|
||||||
_subsume_loads(true),
|
_subsume_loads(true),
|
||||||
|
_do_escape_analysis(false),
|
||||||
_failure_reason(NULL),
|
_failure_reason(NULL),
|
||||||
_code_buffer("Compile::Fill_buffer"),
|
_code_buffer("Compile::Fill_buffer"),
|
||||||
_node_bundling_limit(0),
|
_node_bundling_limit(0),
|
||||||
|
@ -822,7 +832,7 @@ void Compile::Init(int aliaslevel) {
|
||||||
// Type::update_loaded_types(_method, _method->constants());
|
// Type::update_loaded_types(_method, _method->constants());
|
||||||
|
|
||||||
// Init alias_type map.
|
// Init alias_type map.
|
||||||
if (!DoEscapeAnalysis && aliaslevel == 3)
|
if (!_do_escape_analysis && aliaslevel == 3)
|
||||||
aliaslevel = 2; // No unique types without escape analysis
|
aliaslevel = 2; // No unique types without escape analysis
|
||||||
_AliasLevel = aliaslevel;
|
_AliasLevel = aliaslevel;
|
||||||
const int grow_ats = 16;
|
const int grow_ats = 16;
|
||||||
|
|
|
@ -31,6 +31,7 @@ class InlineTree;
|
||||||
class Int_Array;
|
class Int_Array;
|
||||||
class Matcher;
|
class Matcher;
|
||||||
class MachNode;
|
class MachNode;
|
||||||
|
class MachSafePointNode;
|
||||||
class Node;
|
class Node;
|
||||||
class Node_Array;
|
class Node_Array;
|
||||||
class Node_Notes;
|
class Node_Notes;
|
||||||
|
@ -52,9 +53,6 @@ class TypeFunc;
|
||||||
class Unique_Node_List;
|
class Unique_Node_List;
|
||||||
class nmethod;
|
class nmethod;
|
||||||
class WarmCallInfo;
|
class WarmCallInfo;
|
||||||
#ifdef ENABLE_ZAP_DEAD_LOCALS
|
|
||||||
class MachSafePointNode;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
//------------------------------Compile----------------------------------------
|
//------------------------------Compile----------------------------------------
|
||||||
// This class defines a top-level Compiler invocation.
|
// This class defines a top-level Compiler invocation.
|
||||||
|
@ -127,6 +125,7 @@ class Compile : public Phase {
|
||||||
const int _compile_id;
|
const int _compile_id;
|
||||||
const bool _save_argument_registers; // save/restore arg regs for trampolines
|
const bool _save_argument_registers; // save/restore arg regs for trampolines
|
||||||
const bool _subsume_loads; // Load can be matched as part of a larger op.
|
const bool _subsume_loads; // Load can be matched as part of a larger op.
|
||||||
|
const bool _do_escape_analysis; // Do escape analysis.
|
||||||
ciMethod* _method; // The method being compiled.
|
ciMethod* _method; // The method being compiled.
|
||||||
int _entry_bci; // entry bci for osr methods.
|
int _entry_bci; // entry bci for osr methods.
|
||||||
const TypeFunc* _tf; // My kind of signature
|
const TypeFunc* _tf; // My kind of signature
|
||||||
|
@ -260,6 +259,8 @@ class Compile : public Phase {
|
||||||
// instructions that subsume a load may result in an unschedulable
|
// instructions that subsume a load may result in an unschedulable
|
||||||
// instruction sequence.
|
// instruction sequence.
|
||||||
bool subsume_loads() const { return _subsume_loads; }
|
bool subsume_loads() const { return _subsume_loads; }
|
||||||
|
// Do escape analysis.
|
||||||
|
bool do_escape_analysis() const { return _do_escape_analysis; }
|
||||||
bool save_argument_registers() const { return _save_argument_registers; }
|
bool save_argument_registers() const { return _save_argument_registers; }
|
||||||
|
|
||||||
|
|
||||||
|
@ -560,7 +561,7 @@ class Compile : public Phase {
|
||||||
// replacement, entry_bci indicates the bytecode for which to compile a
|
// replacement, entry_bci indicates the bytecode for which to compile a
|
||||||
// continuation.
|
// continuation.
|
||||||
Compile(ciEnv* ci_env, C2Compiler* compiler, ciMethod* target,
|
Compile(ciEnv* ci_env, C2Compiler* compiler, ciMethod* target,
|
||||||
int entry_bci, bool subsume_loads);
|
int entry_bci, bool subsume_loads, bool do_escape_analysis);
|
||||||
|
|
||||||
// Second major entry point. From the TypeFunc signature, generate code
|
// Second major entry point. From the TypeFunc signature, generate code
|
||||||
// to pass arguments from the Java calling convention to the C calling
|
// to pass arguments from the Java calling convention to the C calling
|
||||||
|
|
|
@ -395,6 +395,15 @@ PhiNode *ConnectionGraph::create_split_phi(PhiNode *orig_phi, int alias_idx, Gro
|
||||||
if (result != NULL && C->get_alias_index(result->adr_type()) == alias_idx) {
|
if (result != NULL && C->get_alias_index(result->adr_type()) == alias_idx) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
if ((int)C->unique() + 2*NodeLimitFudgeFactor > MaxNodeLimit) {
|
||||||
|
if (C->do_escape_analysis() == true && !C->failing()) {
|
||||||
|
// Retry compilation without escape analysis.
|
||||||
|
// If this is the first failure, the sentinel string will "stick"
|
||||||
|
// to the Compile object, and the C2Compiler will see it and retry.
|
||||||
|
C->record_failure(C2Compiler::retry_no_escape_analysis());
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
orig_phi_worklist.append_if_missing(orig_phi);
|
orig_phi_worklist.append_if_missing(orig_phi);
|
||||||
result = PhiNode::make(orig_phi->in(0), NULL, Type::MEMORY, atype);
|
result = PhiNode::make(orig_phi->in(0), NULL, Type::MEMORY, atype);
|
||||||
|
@ -443,6 +452,9 @@ PhiNode *ConnectionGraph::split_memory_phi(PhiNode *orig_phi, int alias_idx, Gro
|
||||||
mem = nphi;
|
mem = nphi;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (C->failing()) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
result->set_req(idx++, mem);
|
result->set_req(idx++, mem);
|
||||||
}
|
}
|
||||||
#ifdef ASSERT
|
#ifdef ASSERT
|
||||||
|
@ -672,6 +684,9 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
||||||
if (mem->is_Phi()) {
|
if (mem->is_Phi()) {
|
||||||
mem = split_memory_phi(mem->as_Phi(), alias_idx, orig_phis, igvn);
|
mem = split_memory_phi(mem->as_Phi(), alias_idx, orig_phis, igvn);
|
||||||
}
|
}
|
||||||
|
if (_compile->failing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (mem != n->in(MemNode::Memory))
|
if (mem != n->in(MemNode::Memory))
|
||||||
set_map(n->_idx, mem);
|
set_map(n->_idx, mem);
|
||||||
if (n->is_Load()) {
|
if (n->is_Load()) {
|
||||||
|
@ -742,7 +757,11 @@ void ConnectionGraph::split_unique_types(GrowableArray<Node *> &alloc_worklist)
|
||||||
if((uint)_compile->get_general_index(ni) == i) {
|
if((uint)_compile->get_general_index(ni) == i) {
|
||||||
Node *m = (ni >= nmm->req()) ? nmm->empty_memory() : nmm->in(ni);
|
Node *m = (ni >= nmm->req()) ? nmm->empty_memory() : nmm->in(ni);
|
||||||
if (nmm->is_empty_memory(m)) {
|
if (nmm->is_empty_memory(m)) {
|
||||||
nmm->set_memory_at(ni, split_memory_phi(mem->as_Phi(), ni, orig_phis, igvn));
|
m = split_memory_phi(mem->as_Phi(), ni, orig_phis, igvn);
|
||||||
|
if (_compile->failing()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nmm->set_memory_at(ni, m);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -881,6 +900,11 @@ void ConnectionGraph::compute_escape() {
|
||||||
// Now use the escape information to create unique types for
|
// Now use the escape information to create unique types for
|
||||||
// unescaped objects
|
// unescaped objects
|
||||||
split_unique_types(alloc_worklist);
|
split_unique_types(alloc_worklist);
|
||||||
|
if (_compile->failing()) return;
|
||||||
|
|
||||||
|
// Clean up after split unique types.
|
||||||
|
ResourceMark rm;
|
||||||
|
PhaseRemoveUseless pru(_compile->initial_gvn(), _compile->for_igvn());
|
||||||
}
|
}
|
||||||
|
|
||||||
Node * ConnectionGraph::skip_casts(Node *n) {
|
Node * ConnectionGraph::skip_casts(Node *n) {
|
||||||
|
|
|
@ -1836,7 +1836,7 @@ PhiNode *Parse::ensure_phi(int idx, bool nocreate) {
|
||||||
|
|
||||||
PhiNode* phi = PhiNode::make(region, o, t);
|
PhiNode* phi = PhiNode::make(region, o, t);
|
||||||
gvn().set_type(phi, t);
|
gvn().set_type(phi, t);
|
||||||
if (DoEscapeAnalysis) record_for_igvn(phi);
|
if (C->do_escape_analysis()) record_for_igvn(phi);
|
||||||
map->set_req(idx, phi);
|
map->set_req(idx, phi);
|
||||||
return phi;
|
return phi;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue