diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 1af085cd1282d..b39db528691de 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -398,7 +398,10 @@ PhaseCFG::PhaseCFG(Arena* arena, RootNode* root, Matcher& matcher) Node *x = new GotoNode(nullptr); x->init_req(0, x); _goto = matcher.match_tree(x); - assert(_goto != nullptr, ""); + assert(_goto != nullptr || C->failure_is_artificial(), ""); + if (C->failing()) { + return; + } _goto->set_req(0,_goto); // Build the CFG in Reverse Post Order diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 42de77acca931..c14162ddf6eed 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -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) \ + \ develop(intx, OptoPrologueNops, 0, \ "Insert this many extra nop instructions " \ "in the prologue of every nmethod") \ diff --git a/src/hotspot/share/opto/chaitin.cpp b/src/hotspot/share/opto/chaitin.cpp index 220b916436ea8..be0aadacbc2b9 100644 --- a/src/hotspot/share/opto/chaitin.cpp +++ b/src/hotspot/share/opto/chaitin.cpp @@ -479,6 +479,9 @@ void PhaseChaitin::Register_Allocate() { } uint new_max_lrg_id = Split(_lrg_map.max_lrg_id(), &split_arena); // Split spilling LRG everywhere + if (C->failing()) { + return; + } _lrg_map.set_max_lrg_id(new_max_lrg_id); // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) // or we failed to split @@ -551,6 +554,9 @@ void PhaseChaitin::Register_Allocate() { return; } uint new_max_lrg_id = Split(_lrg_map.max_lrg_id(), &split_arena); // Split spilling LRG everywhere + if (C->failing()) { + return; + } _lrg_map.set_max_lrg_id(new_max_lrg_id); // Bail out if unique gets too large (ie - unique > MaxNodeLimit - 2*NodeLimitFudgeFactor) C->check_node_count(2 * NodeLimitFudgeFactor, "out of nodes after split"); diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 5abc398cb8ce2..dc91d6db1e398 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -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; _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()); 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); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 13ad980434b79..1ccfedd0d460f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -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,6 +835,27 @@ class Compile : public Phase { const CompilationFailureInfo* first_failure_details() const { return _first_failure_details; } + 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 + bool failure_reason_is(const char* r) const { return (r == _failure_reason.get()) || (r != nullptr && @@ -834,11 +863,11 @@ class Compile : public Phase { 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()) { diff --git a/src/hotspot/share/opto/gcm.cpp b/src/hotspot/share/opto/gcm.cpp index 0c7c3ec30c6e9..9ba571b926d0d 100644 --- a/src/hotspot/share/opto/gcm.cpp +++ b/src/hotspot/share/opto/gcm.cpp @@ -1527,8 +1527,8 @@ void PhaseCFG::schedule_late(VectorSet &visited, Node_Stack &stack) { C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { // Bailout without retry when (early->_dom_depth > LCA->_dom_depth) - assert(false, "graph should be schedulable"); - C->record_method_not_compilable("late schedule failed: incorrect graph"); + assert(C->failure_is_artificial(), "graph should be schedulable"); + C->record_method_not_compilable("late schedule failed: incorrect graph" DEBUG_ONLY(COMMA true)); } return; } @@ -1708,8 +1708,8 @@ void PhaseCFG::global_code_motion() { Block* block = get_block(i); if (!schedule_local(block, ready_cnt, visited, recalc_pressure_nodes)) { if (!C->failure_reason_is(C2Compiler::retry_no_subsuming_loads())) { - assert(false, "local schedule failed"); - C->record_method_not_compilable("local schedule failed"); + assert(C->failure_is_artificial(), "local schedule failed"); + C->record_method_not_compilable("local schedule failed" DEBUG_ONLY(COMMA true)); } _regalloc = nullptr; return; diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 1a0d0bd037515..19605dda16c03 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -340,7 +340,9 @@ static inline void add_one_req(Node* dstphi, Node* src) { // having a control input of its exception map, rather than null. Such // regions do not appear except in this function, and in use_exception_state. void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* phi_map) { - if (failing()) return; // dying anyway... + if (failing_internal()) { + return; // dying anyway... + } JVMState* ex_jvms = ex_map->_jvms; assert(ex_jvms->same_calls_as(phi_map->_jvms), "consistent call chains"); assert(ex_jvms->stkoff() == phi_map->_jvms->stkoff(), "matching locals"); @@ -446,7 +448,7 @@ void GraphKit::combine_exception_states(SafePointNode* ex_map, SafePointNode* ph //--------------------------use_exception_state-------------------------------- Node* GraphKit::use_exception_state(SafePointNode* phi_map) { - if (failing()) { stop(); return top(); } + if (failing_internal()) { stop(); return top(); } Node* region = phi_map->control(); Node* hidden_merge_mark = root(); assert(phi_map->jvms()->map() == phi_map, "sanity: 1-1 relation"); @@ -2056,7 +2058,9 @@ Node* GraphKit::uncommon_trap(int trap_request, ciKlass* klass, const char* comment, bool must_throw, bool keep_exact_action) { - if (failing()) stop(); + if (failing_internal()) { + stop(); + } if (stopped()) return nullptr; // trap reachable? // Note: If ProfileTraps is true, and if a deopt. actually diff --git a/src/hotspot/share/opto/graphKit.hpp b/src/hotspot/share/opto/graphKit.hpp index e7f17c72a1b99..421ce933ed1f5 100644 --- a/src/hotspot/share/opto/graphKit.hpp +++ b/src/hotspot/share/opto/graphKit.hpp @@ -82,7 +82,7 @@ class GraphKit : public Phase { #ifdef ASSERT ~GraphKit() { - assert(failing() || !has_exceptions(), + assert(failing_internal() || !has_exceptions(), "unless compilation failed, user must call transfer_exceptions_into_jvms"); } #endif @@ -182,6 +182,7 @@ class GraphKit : public Phase { // Tell if the compilation is failing. bool failing() const { return C->failing(); } + bool failing_internal() const { return C->failing_internal(); } // Set _map to null, signalling a stop to further bytecode execution. // Preserve the map intact for future use, and return it back to the caller. diff --git a/src/hotspot/share/opto/lcm.cpp b/src/hotspot/share/opto/lcm.cpp index 3c6de96074a15..87be6a76eb202 100644 --- a/src/hotspot/share/opto/lcm.cpp +++ b/src/hotspot/share/opto/lcm.cpp @@ -1204,7 +1204,7 @@ bool PhaseCFG::schedule_local(Block* block, GrowableArray& ready_cnt, Vecto // to the Compile object, and the C2Compiler will see it and retry. C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { - assert(false, "graph should be schedulable"); + assert(C->failure_is_artificial(), "graph should be schedulable"); } // assert( phi_cnt == end_idx(), "did not schedule all" ); return false; diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index b2a1a9d5e2102..8cd6fdd8cf5b6 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4935,7 +4935,9 @@ void PhaseIdealLoop::verify() const { bool success = true; PhaseIdealLoop phase_verify(_igvn, this); - if (C->failing()) return; + if (C->failing_internal()) { + return; + } // Verify ctrl and idom of every node. success &= verify_idom_and_nodes(C->root(), &phase_verify); diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 2d169a6459b38..3aa67bcb5cb8b 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1128,7 +1128,9 @@ class PhaseIdealLoop : public PhaseTransform { _verify_only(verify_me == nullptr), _mode(LoopOptsVerify), _nodes_required(UINT_MAX) { + DEBUG_ONLY(C->set_phase_verify_ideal_loop();) build_and_optimize(); + DEBUG_ONLY(C->reset_phase_verify_ideal_loop();) } #endif diff --git a/src/hotspot/share/opto/matcher.cpp b/src/hotspot/share/opto/matcher.cpp index 6d96bff1c1c44..2031b09ca9d18 100644 --- a/src/hotspot/share/opto/matcher.cpp +++ b/src/hotspot/share/opto/matcher.cpp @@ -194,6 +194,9 @@ void Matcher::match( ) { } // One-time initialization of some register masks. init_spill_mask( C->root()->in(1) ); + if (C->failing()) { + return; + } _return_addr_mask = return_addr(); #ifdef _LP64 // Pointers take 2 slots in 64-bit land @@ -287,10 +290,16 @@ void Matcher::match( ) { // preserve area, locks & pad2. OptoReg::Name reg1 = warp_incoming_stk_arg(vm_parm_regs[i].first()); + if (C->failing()) { + return; + } if( OptoReg::is_valid(reg1)) _calling_convention_mask[i].Insert(reg1); OptoReg::Name reg2 = warp_incoming_stk_arg(vm_parm_regs[i].second()); + if (C->failing()) { + return; + } if( OptoReg::is_valid(reg2)) _calling_convention_mask[i].Insert(reg2); @@ -386,7 +395,7 @@ void Matcher::match( ) { // Don't set control, it will confuse GCM since there are no uses. // The control will be set when this node is used first time // in find_base_for_derived(). - assert(_mach_null != nullptr, ""); + assert(_mach_null != nullptr || C->failure_is_artificial(), ""); // bailouts are handled below. C->set_root(xroot->is_Root() ? xroot->as_Root() : nullptr); @@ -404,7 +413,7 @@ void Matcher::match( ) { assert(C->failure_reason() != nullptr, "graph lost: reason unknown"); ss.print("graph lost: reason unknown"); } - C->record_method_not_compilable(ss.as_string()); + C->record_method_not_compilable(ss.as_string() DEBUG_ONLY(COMMA true)); } if (C->failing()) { // delete old; @@ -1439,10 +1448,16 @@ MachNode *Matcher::match_sfpt( SafePointNode *sfpt ) { } // Grab first register, adjust stack slots and insert in mask. OptoReg::Name reg1 = warp_outgoing_stk_arg(first, begin_out_arg_area, out_arg_limit_per_call ); + if (C->failing()) { + return nullptr; + } if (OptoReg::is_valid(reg1)) rm->Insert( reg1 ); // Grab second register (if any), adjust stack slots and insert in mask. OptoReg::Name reg2 = warp_outgoing_stk_arg(second, begin_out_arg_area, out_arg_limit_per_call ); + if (C->failing()) { + return nullptr; + } if (OptoReg::is_valid(reg2)) rm->Insert( reg2 ); } // End of for all arguments @@ -2679,6 +2694,10 @@ bool Matcher::gen_narrow_oop_implicit_null_checks() { // Compute RegMask for an ideal register. const RegMask* Matcher::regmask_for_ideal_register(uint ideal_reg, Node* ret) { + assert(!C->failing_internal() || C->failure_is_artificial(), "already failing."); + if (C->failing()) { + return nullptr; + } const Type* t = Type::mreg2type[ideal_reg]; if (t == nullptr) { assert(ideal_reg >= Op_VecA && ideal_reg <= Op_VecZ, "not a vector: %d", ideal_reg); @@ -2709,7 +2728,10 @@ const RegMask* Matcher::regmask_for_ideal_register(uint ideal_reg, Node* ret) { default: ShouldNotReachHere(); } MachNode* mspill = match_tree(spill); - assert(mspill != nullptr, "matching failed: %d", ideal_reg); + assert(mspill != nullptr || C->failure_is_artificial(), "matching failed: %d", ideal_reg); + if (C->failing()) { + return nullptr; + } // Handle generic vector operand case if (Matcher::supports_generic_vector_operands && t->isa_vect()) { specialize_mach_node(mspill); @@ -2855,7 +2877,7 @@ bool Matcher::is_encode_and_store_pattern(const Node* n, const Node* m) { #ifdef ASSERT bool Matcher::verify_after_postselect_cleanup() { - assert(!C->failing(), "sanity"); + assert(!C->failing_internal() || C->failure_is_artificial(), "sanity"); if (supports_generic_vector_operands) { Unique_Node_List useful; C->identify_useful_nodes(useful); diff --git a/src/hotspot/share/opto/output.cpp b/src/hotspot/share/opto/output.cpp index 260f887347f18..eda0f65d6bc1f 100644 --- a/src/hotspot/share/opto/output.cpp +++ b/src/hotspot/share/opto/output.cpp @@ -1715,7 +1715,7 @@ void PhaseOutput::fill_buffer(C2_MacroAssembler* masm, uint* blk_starts) { node_offsets[n->_idx] = masm->offset(); } #endif - assert(!C->failing(), "Should not reach here if failing."); + assert(!C->failing_internal() || C->failure_is_artificial(), "Should not reach here if failing."); // "Normal" instruction case DEBUG_ONLY(uint instr_offset = masm->offset()); @@ -3393,7 +3393,7 @@ uint PhaseOutput::scratch_emit_size(const Node* n) { n->emit(&masm, C->regalloc()); // Emitting into the scratch buffer should not fail - assert (!C->failing(), "Must not have pending failure. Reason is: %s", C->failure_reason()); + assert(!C->failing_internal() || C->failure_is_artificial(), "Must not have pending failure. Reason is: %s", C->failure_reason()); if (is_branch) // Restore label. n->as_MachBranch()->label_set(saveL, save_bnum); diff --git a/src/hotspot/share/opto/parse.hpp b/src/hotspot/share/opto/parse.hpp index 484c49367cc4d..039283bc863d1 100644 --- a/src/hotspot/share/opto/parse.hpp +++ b/src/hotspot/share/opto/parse.hpp @@ -426,7 +426,7 @@ class Parse : public GraphKit { void set_parse_bci(int bci); // Must this parse be aborted? - bool failing() { return C->failing(); } + bool failing() const { return C->failing_internal(); } // might have cascading effects, not stressing bailouts for now. Block* rpo_at(int rpo) { assert(0 <= rpo && rpo < _block_count, "oob"); diff --git a/src/hotspot/share/opto/reg_split.cpp b/src/hotspot/share/opto/reg_split.cpp index 9f89c683b34b5..6d948aff011cf 100644 --- a/src/hotspot/share/opto/reg_split.cpp +++ b/src/hotspot/share/opto/reg_split.cpp @@ -306,8 +306,8 @@ static Node* clone_node(Node* def, Block *b, Compile* C) { C->record_failure(C2Compiler::retry_no_subsuming_loads()); } else { // Bailout without retry - assert(false, "RA Split failed: attempt to clone node with anti_dependence"); - C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence"); + assert(C->failure_is_artificial(), "RA Split failed: attempt to clone node with anti_dependence"); + C->record_method_not_compilable("RA Split failed: attempt to clone node with anti_dependence" DEBUG_ONLY(COMMA true)); } return nullptr; } diff --git a/test/hotspot/jtreg/compiler/debug/TestStressBailout.java b/test/hotspot/jtreg/compiler/debug/TestStressBailout.java new file mode 100644 index 0000000000000..68610576a390f --- /dev/null +++ b/test/hotspot/jtreg/compiler/debug/TestStressBailout.java @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package compiler.debug; + +import java.util.Random; + +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; +import jdk.test.lib.Utils; + +/* + * @test + * @key stress randomness + * @bug 8330157 + * @requires vm.debug == true & vm.compiler2.enabled & (vm.opt.AbortVMOnCompilationFailure == "null" | !vm.opt.AbortVMOnCompilationFailure) + * @summary Basic tests for bailout stress flag. + * @library /test/lib / + * @run driver compiler.debug.TestStressBailout + */ + +public class TestStressBailout { + + static void runTest(int invprob) throws Exception { + String[] procArgs = {"-Xcomp", "-XX:-TieredCompilation", "-XX:+StressBailout", + "-XX:StressBailoutMean=" + invprob, "-version"}; + ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(procArgs); + OutputAnalyzer out = new OutputAnalyzer(pb.start()); + out.shouldHaveExitValue(0); + } + + public static void main(String[] args) throws Exception { + Random r = Utils.getRandomInstance(); + // Likely bail out on -version, for some low Mean value. + runTest(r.nextInt(1, 10)); + // Higher value + runTest(r.nextInt(10, 1_000_000)); + } +} diff --git a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java index d62e286c68d58..e2c7230bc0dc9 100644 --- a/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java +++ b/test/hotspot/jtreg/testlibrary/ctw/src/sun/hotspot/tools/ctw/CtwRunner.java @@ -309,6 +309,13 @@ private String[] cmd(long classStart, long classStop) { // allocation allocate lots of memory "-XX:CompileCommand=memlimit,*.*,0")); + // Use this stress mode 10% of the time as it could make some long-running compilations likely to abort. + if (rng.nextInt(10) == 0) { + Args.add("-XX:+StressBailout"); + Args.add("-XX:StressBailoutMean=100000"); + Args.add("-XX:+CaptureBailoutInformation"); + } + for (String arg : CTW_EXTRA_ARGS.split(",")) { Args.add(arg); }