-
Couldn't load subscription status.
- Fork 6.1k
8330157: C2: Add a stress flag for bailouts #19646
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
d0b9b4a
bf2d06b
9adda89
c44c536
42b8836
4ded3d6
ad97f6c
5ebc2b2
e71c357
9bd2b17
b2b8108
f1fd6b9
d90e806
092df75
da4e5f7
9245b2c
fbcf582
fe407f1
d514485
56cc64f
1a17c69
2708831
4c7cf93
fac4810
da8d20e
7b15e17
31f075a
43eb389
4bbd8ab
79da03c
29642d2
26bffb7
f1751b0
c2a4304
c5b1223
0da2e6d
db32550
d3f300f
88d8081
9ab95aa
c23e4a4
14f6475
d91bc06
cb748fb
b6eb9a8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,6 +70,14 @@ | |
| develop(bool, StressMethodHandleLinkerInlining, false, \ | ||
| "Stress inlining through method handle linkers") \ | ||
| \ | ||
| develop(bool, StressBailout, false, \ | ||
| "Perform bailouts randomly at C2 failing() checks") \ | ||
| \ | ||
| develop(uint, StressBailoutMean, 100000, \ | ||
| "The expected number of failing() checks made until " \ | ||
| "a random bailout.") \ | ||
| range(1, max_juint) \ | ||
| \ | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "Interval" is usually a time period. Maybe |
||
| develop(intx, OptoPrologueNops, 0, \ | ||
| "Insert this many extra nop instructions " \ | ||
| "in the prologue of every nmethod") \ | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -720,7 +720,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, | |
| } | ||
|
|
||
| if (StressLCM || StressGCM || StressIGVN || StressCCP || | ||
| StressIncrementalInlining || StressMacroExpansion || StressUnstableIfTraps) { | ||
| StressIncrementalInlining || StressMacroExpansion || StressUnstableIfTraps || StressBailout) { | ||
| initialize_stress_seed(directive); | ||
| } | ||
|
|
||
|
|
@@ -798,7 +798,7 @@ Compile::Compile( ciEnv* ci_env, ciMethod* target, int osr_bci, | |
| assert(failure_reason() != nullptr, "expect reason for parse failure"); | ||
| stringStream ss; | ||
| ss.print("method parse failed: %s", failure_reason()); | ||
| record_method_not_compilable(ss.as_string()); | ||
| record_method_not_compilable(ss.as_string() DEBUG_ONLY(COMMA true)); | ||
| return; | ||
| } | ||
| GraphKit kit(jvms); | ||
|
|
@@ -973,7 +973,7 @@ Compile::Compile( ciEnv* ci_env, | |
| _types = new (comp_arena()) Type_Array(comp_arena()); | ||
| _node_hash = new (comp_arena()) NodeHash(comp_arena(), 255); | ||
|
|
||
| if (StressLCM || StressGCM) { | ||
| if (StressLCM || StressGCM || StressBailout) { | ||
| initialize_stress_seed(directive); | ||
| } | ||
|
|
||
|
|
@@ -1018,6 +1018,7 @@ void Compile::Init(bool aliasing) { | |
|
|
||
| #ifdef ASSERT | ||
| _phase_optimize_finished = false; | ||
| _phase_verify_ideal_loop = false; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is this for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is to disable the stress mode during PhaseIdealLoop::verify(). I am not sure if we need to enable the stress mode during this verification pass, and it did not seem trivial to make it work. What do you think? |
||
| _exception_backedge = false; | ||
| _type_verify = nullptr; | ||
| #endif | ||
|
|
@@ -1108,7 +1109,7 @@ void Compile::Init(bool aliasing) { | |
| #ifdef ASSERT | ||
| // Verify that the current StartNode is valid. | ||
| void Compile::verify_start(StartNode* s) const { | ||
| assert(failing() || s == start(), "should be StartNode"); | ||
| assert(failing_internal() || s == start(), "should be StartNode"); | ||
| } | ||
| #endif | ||
|
|
||
|
|
@@ -1118,7 +1119,7 @@ void Compile::verify_start(StartNode* s) const { | |
| * the ideal graph. | ||
| */ | ||
| StartNode* Compile::start() const { | ||
| assert (!failing(), "Must not have pending failure. Reason is: %s", failure_reason()); | ||
| assert (!failing_internal() || C->failure_is_artificial(), "Must not have pending failure. Reason is: %s", failure_reason()); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Having the stress mode in debug builds requires weakening asserts since debug builds assert these paths are not taken. |
||
| for (DUIterator_Fast imax, i = root()->fast_outs(imax); i < imax; i++) { | ||
| Node* start = root()->fast_out(i); | ||
| if (start->is_Start()) { | ||
|
|
@@ -2114,7 +2115,7 @@ void Compile::inline_incrementally(PhaseIterGVN& igvn) { | |
| igvn_worklist()->ensure_empty(); // should be done with igvn | ||
|
|
||
| while (inline_incrementally_one()) { | ||
| assert(!failing(), "inconsistent"); | ||
| assert(!failing_internal() || failure_is_artificial(), "inconsistent"); | ||
| } | ||
| if (failing()) return; | ||
|
|
||
|
|
@@ -2157,7 +2158,7 @@ void Compile::process_late_inline_calls_no_inline(PhaseIterGVN& igvn) { | |
| igvn_worklist()->ensure_empty(); // should be done with igvn | ||
|
|
||
| while (inline_incrementally_one()) { | ||
| assert(!failing(), "inconsistent"); | ||
| assert(!failing_internal() || failure_is_artificial(), "inconsistent"); | ||
| } | ||
| if (failing()) return; | ||
|
|
||
|
|
@@ -2944,6 +2945,9 @@ void Compile::Code_Gen() { | |
|
|
||
| // Build a proper-looking CFG | ||
| PhaseCFG cfg(node_arena(), root(), matcher); | ||
| if (failing()) { | ||
| return; | ||
| } | ||
| _cfg = &cfg; | ||
| { | ||
| TracePhase tp("scheduler", &timers[_t_scheduler]); | ||
|
|
@@ -4329,7 +4333,7 @@ void Compile::verify_graph_edges(bool no_dead_code) { | |
| // to backtrack and retry without subsuming loads. Other than this backtracking | ||
| // behavior, the Compile's failure reason is quietly copied up to the ciEnv | ||
| // by the logic in C2Compiler. | ||
| void Compile::record_failure(const char* reason) { | ||
| void Compile::record_failure(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures)) { | ||
| if (log() != nullptr) { | ||
| log()->elem("failure reason='%s' phase='compile'", reason); | ||
| } | ||
|
|
@@ -4339,6 +4343,8 @@ void Compile::record_failure(const char* reason) { | |
| if (CaptureBailoutInformation) { | ||
| _first_failure_details = new CompilationFailureInfo(reason); | ||
| } | ||
| } else { | ||
| assert(!StressBailout || allow_multiple_failures, "should have handled previous failure."); | ||
| } | ||
|
|
||
| if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { | ||
|
|
@@ -4366,7 +4372,9 @@ Compile::TracePhase::TracePhase(const char* name, elapsedTimer* accumulator) | |
| } | ||
|
|
||
| Compile::TracePhase::~TracePhase() { | ||
| if (_compile->failing()) return; | ||
| if (_compile->failing_internal()) { | ||
| return; // timing code, not stressing bailouts. | ||
| } | ||
| #ifdef ASSERT | ||
| if (PrintIdealNodeCount) { | ||
| tty->print_cr("phase name='%s' nodes='%d' live='%d' live_graph_walk='%d'", | ||
|
|
@@ -5057,6 +5065,22 @@ bool Compile::randomized_select(int count) { | |
| return (random() & RANDOMIZED_DOMAIN_MASK) < (RANDOMIZED_DOMAIN / count); | ||
| } | ||
|
|
||
| #ifdef ASSERT | ||
| // Failures are geometrically distributed with probability 1/StressBailoutMean. | ||
| bool Compile::fail_randomly() { | ||
| if ((random() % StressBailoutMean) != 0) { | ||
| return false; | ||
| } | ||
| record_failure("StressBailout"); | ||
| return true; | ||
| } | ||
|
|
||
| bool Compile::failure_is_artificial() { | ||
| assert(failing_internal(), "should be failing"); | ||
| return C->failure_reason_is("StressBailout"); | ||
| } | ||
| #endif | ||
|
|
||
| CloneMap& Compile::clone_map() { return _clone_map; } | ||
| void Compile::set_clone_map(Dict* d) { _clone_map._dict = d; } | ||
|
|
||
|
|
@@ -5144,7 +5168,7 @@ void Compile::sort_macro_nodes() { | |
| } | ||
|
|
||
| void Compile::print_method(CompilerPhaseType cpt, int level, Node* n) { | ||
| if (failing()) { return; } | ||
| if (failing_internal()) { return; } // failing_internal to not stress bailouts from printing code. | ||
| EventCompilerPhase event(UNTIMED); | ||
| if (event.should_commit()) { | ||
| CompilerEvent::PhaseEvent::post(event, C->_latest_stage_start_counter, cpt, C->_compile_id, level); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -391,6 +391,8 @@ class Compile : public Phase { | |
| DEBUG_ONLY(Unique_Node_List* _modified_nodes;) // List of nodes which inputs were modified | ||
| DEBUG_ONLY(bool _phase_optimize_finished;) // Used for live node verification while creating new nodes | ||
|
|
||
| DEBUG_ONLY(bool _phase_verify_ideal_loop;) // Are we in PhaseIdealLoop verification? | ||
|
|
||
| // Arenas for new-space and old-space nodes. | ||
| // Swapped between using _node_arena. | ||
| // The lifetime of the old-space nodes is during xform. | ||
|
|
@@ -786,6 +788,12 @@ class Compile : public Phase { | |
| void set_post_loop_opts_phase() { _post_loop_opts_phase = true; } | ||
| void reset_post_loop_opts_phase() { _post_loop_opts_phase = false; } | ||
|
|
||
| #ifdef ASSERT | ||
| bool phase_verify_ideal_loop() const { return _phase_verify_ideal_loop; } | ||
| void set_phase_verify_ideal_loop() { _phase_verify_ideal_loop = true; } | ||
| void reset_phase_verify_ideal_loop() { _phase_verify_ideal_loop = false; } | ||
| #endif | ||
|
|
||
| bool allow_macro_nodes() { return _allow_macro_nodes; } | ||
| void reset_allow_macro_nodes() { _allow_macro_nodes = false; } | ||
|
|
||
|
|
@@ -815,7 +823,7 @@ class Compile : public Phase { | |
| ciEnv* env() const { return _env; } | ||
| CompileLog* log() const { return _log; } | ||
|
|
||
| bool failing() const { | ||
| bool failing_internal() const { | ||
| return _env->failing() || | ||
| _failure_reason.get() != nullptr; | ||
| } | ||
|
|
@@ -827,18 +835,39 @@ class Compile : public Phase { | |
|
|
||
| const CompilationFailureInfo* first_failure_details() const { return _first_failure_details; } | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enabling the stress code in debug builds too could work. I can give it a try, but it will require some time for more testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Enabling the new flag in debug builds works if we weaken some existing asserts to allow artificial failures. I'm considering making this stress mode debug-only. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I totally support making the stress mode work in debug builds, this is the mode in which we would get most value out of the bailout stress functionality. Support for product builds is secondary in my opinion (although it may add some value, e.g. helping reproduce issues that only surface in product builds), and may be excluded if there are any concerns about e.g. stability. |
||
| bool failing() { | ||
| if (failing_internal()) { | ||
| return true; | ||
| } | ||
| #ifdef ASSERT | ||
| // Disable stress code for PhaseIdealLoop verification (would have cascading effects). | ||
| if (phase_verify_ideal_loop()) { | ||
| return false; | ||
| } | ||
| if (StressBailout) { | ||
| return fail_randomly(); | ||
| } | ||
| #endif | ||
| return false; | ||
| } | ||
|
|
||
| #ifdef ASSERT | ||
| bool fail_randomly(); | ||
| bool failure_is_artificial(); | ||
| #endif | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this function really have to be inline? We only exercise it for StressBailout=true |
||
| bool failure_reason_is(const char* r) const { | ||
| return (r == _failure_reason.get()) || | ||
| (r != nullptr && | ||
| _failure_reason.get() != nullptr && | ||
| strcmp(r, _failure_reason.get()) == 0); | ||
| } | ||
|
|
||
| void record_failure(const char* reason); | ||
| void record_method_not_compilable(const char* reason) { | ||
| void record_failure(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures = false)); | ||
| void record_method_not_compilable(const char* reason DEBUG_ONLY(COMMA bool allow_multiple_failures = false)) { | ||
| env()->record_method_not_compilable(reason); | ||
| // Record failure reason. | ||
| record_failure(reason); | ||
| record_failure(reason DEBUG_ONLY(COMMA allow_multiple_failures)); | ||
| } | ||
| bool check_node_count(uint margin, const char* reason) { | ||
| if (oom()) { | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new flag found several missing
failing()checks, which are added in this patch.