From 9cd31067a891ef4b324f66622c863eebfb1435b4 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 20:59:29 +0200 Subject: [PATCH 01/40] add flag for C2 Optimization Tracing (debug only) --- src/hotspot/share/opto/c2_globals.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 3a2d4cbdf9691..7cf7ab561b651 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -650,6 +650,9 @@ product(bool, TraceTypeProfile, false, DIAGNOSTIC, \ "Trace type profile") \ \ + develop(bool, TraceC2Optimizations, false, \ + "Trace selected C2 loop optimizations (counts only)") \ + \ develop(bool, PoisonOSREntry, true, \ "Detect abnormal calls to OSR code") \ \ From d192c098e2f14390da176b46e9d28668e8fe7fb2 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:00:37 +0200 Subject: [PATCH 02/40] add canonicalization hook --- src/hotspot/share/opto/phaseX.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 1df2cdb179e52..fe0c341d6df12 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2170,6 +2170,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { Node* con = makecon(t); // Make a constant add_users_to_worklist(k); subsume_node(k, con); // Everybody using k now uses con + C->record_optimization_event(OptEvent_Canonicalization); return con; } @@ -2179,6 +2180,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i + C->record_optimization_event(OptEvent_Canonicalization); return i; } @@ -2189,10 +2191,12 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i + C->record_optimization_event(OptEvent_Canonicalization); return i; } // Return Idealized original + C->record_optimization_event(OptEvent_Canonicalization); return k; } From 9b02d476184c22b81d3ecd19945ed34439df02bb Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:01:10 +0200 Subject: [PATCH 03/40] eliminate autobox hooks --- src/hotspot/share/opto/macro.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index a0b52358bac8e..75d146b715333 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2422,6 +2422,9 @@ void PhaseMacroExpand::eliminate_macro_nodes() { break; case Node::Class_CallStaticJava: success = eliminate_boxing_node(n->as_CallStaticJava()); + if (success) { + C->record_optimization_event(OptEvent_EliminateAutobox); + } break; case Node::Class_Lock: case Node::Class_Unlock: From b76e81f2ebf83a033e6e99099b01203513237519 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:01:25 +0200 Subject: [PATCH 04/40] Eliminate Autobox hook --- src/hotspot/share/opto/macro.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 75d146b715333..15ef55c1b8cbc 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1099,6 +1099,9 @@ bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { } process_users_of_allocation(alloc); + if (boxing_alloc) { + C->record_optimization_event(OptEvent_EliminateAutobox); + } #ifndef PRODUCT if (PrintEliminateAllocations) { From 005276e6c3df8f387fad96a25ee03b29292a15a2 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:01:54 +0200 Subject: [PATCH 05/40] CCP opt hook added for trace c2 opts --- src/hotspot/share/opto/phaseX.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index fe0c341d6df12..c61d243c55283 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -3181,6 +3181,7 @@ Node *PhaseCCP::transform_once( Node *n ) { if( !n->is_Con() ) { if( t != Type::TOP ) { nn = makecon(t); // ConNode::make(t); + C->record_optimization_event(OptEvent_ConditionalConstantPropagation); NOT_PRODUCT( inc_constants(); ) } else if( n->is_Region() ) { // Unreachable region // Note: nn == C->top() From c292b2e58aad52b5254c0288b50a92951623ea12 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:03:29 +0200 Subject: [PATCH 06/40] escape analysis hook --- src/hotspot/share/opto/escape.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 1a5bddd332ead..82dc8c46c53f3 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -118,6 +118,7 @@ void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) { if (congraph->compute_escape()) { // There are non escaping objects. C->set_congraph(congraph); + C->record_optimization_event(OptEvent_EscapeAnalysis); } // Cleanup. if (oop_null->outcnt() == 0) { From dcea212a52abc2d81806bf02c335a821f2588dd6 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:03:41 +0200 Subject: [PATCH 07/40] loop unswitch hook --- src/hotspot/share/opto/loopUnswitch.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index a8d9a5d740bc4..4bc21801f998e 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -405,6 +405,7 @@ void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) { increment_unswitch_counts(original_head, new_head); NOT_PRODUCT(trace_loop_unswitching_result(unswitched_loop_selector, original_head, new_head);) + C->record_optimization_event(OptEvent_LoopUnswitching); C->print_method(PHASE_AFTER_LOOP_UNSWITCHING, 4, new_head); C->set_major_progress(); } @@ -684,4 +685,3 @@ void PhaseIdealLoop::increment_unswitch_counts(LoopNode* original_head, LoopNode original_head->set_unswitch_count(unswitch_count); new_head->set_unswitch_count(unswitch_count); } - From a475111528b2a3532150b1a9ec265deae998b09a Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:04:01 +0200 Subject: [PATCH 08/40] splitif hook --- src/hotspot/share/opto/loopopts.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index a09eef0bb810d..55348865cbcfe 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1472,6 +1472,7 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { tty->print_cr("Split-If"); } do_split_if(iff); + C->record_optimization_event(OptEvent_SplitIf); C->print_method(PHASE_AFTER_SPLIT_IF, 4, iff); return; } From 4281d926191534a1a7cba6c4d2175467973f9b0a Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:04:14 +0200 Subject: [PATCH 09/40] ptr compare hook --- src/hotspot/share/opto/escape.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index 82dc8c46c53f3..2268b1e7f15e4 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -3250,6 +3250,7 @@ void ConnectionGraph::optimize_ideal_graph(GrowableArray& ptr_cmp_worklis } } #endif + C->record_optimization_event(OptEvent_OptimizePtrCompare); igvn->replace_node(n, cmp); } } From 55cdff9e1ba21d402f8cb44c8f9a921c6195bd22 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:04:30 +0200 Subject: [PATCH 10/40] null check elim hook --- src/hotspot/share/opto/graphKit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index d65239ab0f89a..682b5c9a766a8 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1309,6 +1309,7 @@ Node* GraphKit::null_check_common(Node* value, BasicType type, // If so, then the value is already null. if (t->higher_equal(TypePtr::NULL_PTR)) { NOT_PRODUCT(explicit_null_checks_elided++); + C->record_optimization_event(OptEvent_NullCheckElimination); return value; // Elided null assert quickly! } } else { @@ -1318,6 +1319,7 @@ Node* GraphKit::null_check_common(Node* value, BasicType type, if (t->meet(TypePtr::NULL_PTR) != t->remove_speculative()) { // same as: if (!TypePtr::NULL_PTR->higher_equal(t)) ... NOT_PRODUCT(explicit_null_checks_elided++); + C->record_optimization_event(OptEvent_NullCheckElimination); return value; // Elided null check quickly! } } From b0502b708eeaeb645b39a328c5e4d97dd2b97faa Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:04:45 +0200 Subject: [PATCH 11/40] CEE hook --- src/hotspot/share/opto/ifnode.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index b397c2c5852e4..3954c3670e189 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -29,6 +29,7 @@ #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/connode.hpp" +#include "opto/compile.hpp" #include "opto/loopnode.hpp" #include "opto/phaseX.hpp" #include "opto/predicates_enums.hpp" @@ -1794,7 +1795,8 @@ static int subsuming_bool_test_encode(Node* node) { Node* IfProjNode::Identity(PhaseGVN* phase) { // Can only optimize if cannot go the other way const TypeTuple *t = phase->type(in(0))->is_tuple(); - if (t == TypeTuple::IFNEITHER || (always_taken(t) && + bool taken = always_taken(t); + if (t == TypeTuple::IFNEITHER || (taken && // During parsing (GVN) we don't remove dead code aggressively. // Cut off dead branch and let PhaseRemoveUseless take care of it. (!phase->is_IterGVN() || @@ -1805,6 +1807,9 @@ Node* IfProjNode::Identity(PhaseGVN* phase) { // will cause this node to be reprocessed once the dead branch is killed. in(0)->outcnt() == 1))) { // IfNode control + if (taken) { + phase->C->record_optimization_event(OptEvent_ConditionalExpressionElimination); + } if (in(0)->is_BaseCountedLoopEnd()) { // CountedLoopEndNode may be eliminated by if subsuming, replace CountedLoopNode with LoopNode to // avoid mismatching between CountedLoopNode and CountedLoopEndNode in the following optimization. From c9d3dc9855098ccab945f47a30cd22a046088fa6 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:04:59 +0200 Subject: [PATCH 12/40] lock elim hook --- src/hotspot/share/opto/macro.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 15ef55c1b8cbc..5c43ebee6a102 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2129,6 +2129,7 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { #endif alock->log_lock_optimization(C, "eliminate_lock"); + C->record_optimization_event(OptEvent_EliminateLocks); #ifndef PRODUCT if (PrintEliminateLocks) { From f763f8a99f4896a1b7a65e13a47e1dd452edc1f5 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:05:10 +0200 Subject: [PATCH 13/40] deopt hook --- src/hotspot/share/opto/graphKit.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index 682b5c9a766a8..a9da13d0a08ba 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -2089,6 +2089,8 @@ Node* GraphKit::uncommon_trap(int trap_request, } if (stopped()) return nullptr; // trap reachable? + C->record_optimization_event(OptEvent_Deoptimization); + // Note: If ProfileTraps is true, and if a deopt. actually // occurs here, the runtime will make sure an MDO exists. There is // no need to call method()->ensure_method_data() at this point. From 60b62770615ed2585071359a45bbe93c827fee16 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:05:35 +0200 Subject: [PATCH 14/40] simplify phi hook --- src/hotspot/share/opto/cfgnode.cpp | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index ef912ff471ac3..6a3667f1f5736 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -30,6 +30,7 @@ #include "opto/addnode.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" @@ -2144,6 +2145,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2157,10 +2159,12 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { uin = unique_input(phase, true); } if (uin == top) { // Simplest case: no alive inputs. - if (can_reshape) // IGVN transformation + if (can_reshape) { // IGVN transformation + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; - else + } else { return nullptr; // Identity will return TOP + } } else if (uin != nullptr) { // Only one not-null unique input path is left. // Determine if this input is backedge of a loop. @@ -2169,11 +2173,13 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2246,6 +2252,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2279,13 +2286,16 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt != nullptr ) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. - if( can_reshape ) + if( can_reshape ) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; + } // We can't return top if we are in Parse phase - cut inputs only // to stop further optimizations for this phi. Identity will return TOP. assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2490,6 +2500,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2523,6 +2534,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -3133,4 +3145,3 @@ void BlackholeNode::format(PhaseRegAlloc* ra, outputStream* st) const { st->cr(); } #endif - From 72ee6fb01600fe272945d917b9703f8c819530c2 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:06:12 +0200 Subject: [PATCH 15/40] inline hook --- src/hotspot/share/opto/callGenerator.cpp | 1 + src/hotspot/share/opto/doCall.cpp | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index e09d8cabe2c8f..6376721a1e45a 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -707,6 +707,7 @@ void CallGenerator::do_late_inline_helper() { C->env()->notice_inlined_method(inline_cg()->method()); } C->set_inlining_progress(true); + C->record_optimization_event(OptEvent_FunctionInlining); C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup kit.replace_call(call, result, true, do_asserts); } diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index ad7b64f93f5a0..7165396ad4a5a 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -698,6 +698,9 @@ void Parse::do_call() { if (cg->is_inline()) { // Accumulate has_loops estimate C->env()->notice_inlined_method(cg->method()); + if (!cg->is_late_inline()) { + C->record_optimization_event(OptEvent_FunctionInlining); + } } // Reset parser state from [new_]jvms, which now carries results of the call. From 3b2d0746e1c470908b5f623b7004232fd1b76af6 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:06:26 +0200 Subject: [PATCH 16/40] block elim hook --- src/hotspot/share/opto/block.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 7d3d4ec16f4f1..db8c3315b5ff2 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -27,6 +27,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "opto/block.hpp" +#include "opto/compile.hpp" #include "opto/cfgnode.hpp" #include "opto/chaitin.hpp" #include "opto/loopnode.hpp" @@ -1017,6 +1018,7 @@ void PhaseCFG::remove_unreachable_blocks() { get_block(i)->_pre_order--; } _blocks.remove(dead->_pre_order); + C->record_optimization_event(OptEvent_BlockElimination); _number_of_blocks--; // Update the successors' predecessor list and push new unreachable blocks. for (uint i = 0; i < dead->_num_succs; i++) { From c44d2775ae75dd56c4f94019ad17eda0fbed5db2 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:06:49 +0200 Subject: [PATCH 17/40] range check elim hook --- src/hotspot/share/opto/loopTransform.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 5f5e0520e7eb6..395e614b54db0 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2574,6 +2574,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { #endif assert(RangeCheckElimination, ""); + bool eliminated_range_check = false; CountedLoopNode *cl = loop->_head->as_CountedLoop(); // protect against stride not being a constant @@ -2747,6 +2748,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { // sense of the test. C->print_method(PHASE_BEFORE_RANGE_CHECK_ELIMINATION, 4, iff); + eliminated_range_check = true; // Perform the limit computations in jlong to avoid overflow jlong lscale_con = scale_con; @@ -2926,6 +2928,9 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { assert(is_dominator(new_limit_ctrl, get_ctrl(iffm->in(1)->in(1))), "control of cmp should be below control of updated input"); C->print_method(PHASE_AFTER_RANGE_CHECK_ELIMINATION, 4, cl); + if (eliminated_range_check) { + C->record_optimization_event(OptEvent_RangeCheckElimination); + } } // Adjust control for node and its inputs (and inputs of its inputs) to be above the pre end From c2488b0e86bc48266dec873447f7c06a1d61cd4f Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:07:01 +0200 Subject: [PATCH 18/40] missing header for hook --- src/hotspot/share/opto/doCall.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index 7165396ad4a5a..822260340c15c 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -37,6 +37,7 @@ #include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/mulnode.hpp" #include "opto/parse.hpp" #include "opto/rootnode.hpp" From 3d64afb37b612dc9331ab3d680fbb89b1195a003 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:07:12 +0200 Subject: [PATCH 19/40] loop unrolling hook --- src/hotspot/share/opto/loopTransform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 395e614b54db0..b765751dea69f 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2131,6 +2131,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj } #endif + C->record_optimization_event(OptEvent_LoopUnrolling); C->print_method(PHASE_AFTER_LOOP_UNROLLING, 4, clone_head); } From c66a68a83434fea226e79a57d8986c5483223a41 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:07:23 +0200 Subject: [PATCH 20/40] loop peeling hock --- src/hotspot/share/opto/loopTransform.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index b765751dea69f..04d55f6b9bd0a 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -777,6 +777,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { C->set_major_progress(); + C->record_optimization_event(OptEvent_LoopPeeling); // Peeling a 'main' loop in a pre/main/post situation obfuscates the // 'pre' loop from the main and the 'pre' can no longer have its // iterations adjusted. Therefore, we need to declare this loop as From e0fd583884a891e27abbaf0400ee8a6bd0adbe32 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:07:44 +0200 Subject: [PATCH 21/40] parallel iv hook --- src/hotspot/share/opto/loopnode.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index 2c604e6d4783b..a35ebaabdf1cd 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4438,6 +4438,7 @@ void PhaseIdealLoop::replace_parallel_iv(IdealLoopTree *loop) { set_ctrl(add, cl); _igvn.replace_node( phi2, add ); + C->record_optimization_event(OptEvent_ParallelInductionVars); // Sometimes an induction variable is unused if (add->outcnt() == 0) { _igvn.remove_dead_node(add); From 93be05e8952a823c42b4b9b2cc9a785302df0933 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:08:04 +0200 Subject: [PATCH 22/40] implement printing of c2 optimization --- src/hotspot/share/opto/compile.cpp | 121 ++++++++++++++++++++++++++++- src/hotspot/share/opto/compile.hpp | 29 +++++++ 2 files changed, 149 insertions(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index cef7aa612192c..93bc01b29de58 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -24,6 +24,7 @@ #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" +#include "ci/ciInstanceKlass.hpp" #include "ci/ciReplay.hpp" #include "classfile/javaClasses.hpp" #include "code/aotCodeCache.hpp" @@ -80,6 +81,7 @@ #include "opto/type.hpp" #include "opto/vector.hpp" #include "opto/vectornode.hpp" +#include "opto/c2_globals.hpp" #include "runtime/globals_extension.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" @@ -90,6 +92,30 @@ #include "utilities/hashTable.hpp" #include "utilities/macros.hpp" +#ifndef PRODUCT +static const char* const optimization_event_names[OptEvent_Count] = { + "Loop Unrolling", + "Loop Peeling", + "Parallel Induction Variables", + "Split If", + "Loop Unswitching", + "Conditional Expression Elimination", + "Function Inlining", + "Deoptimization", + "Escape Analysis", + "Eliminate Locks", + "Locks Coarsening", + "Conditional Constant Propagation", + "Eliminate Autobox", + "Block Elimination", + "Simplify Phi Function", + "Canonicalization", + "Null Check Elimination", + "Range Check Elimination", + "Optimize Ptr Compare" +}; +#endif + // -------------------- Compile::mach_constant_base_node ----------------------- // Constant table base node singleton. MachConstantBaseNode* Compile::mach_constant_base_node() { @@ -706,6 +732,10 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, TraceTime t1("Total compilation time", &_t_totalCompilation, CITime, CITimeVerbose); TraceTime t2(nullptr, &_t_methodCompilation, CITime, false); +#ifndef PRODUCT + Copy::zero_to_bytes(_optimization_counters, sizeof(_optimization_counters)); +#endif + #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) bool print_opto_assembly = directive->PrintOptoAssemblyOption; // We can always print a disassembly, either abstract (hex dump) or @@ -890,6 +920,41 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // Now generate code Code_Gen(); + if (TraceC2Optimizations) { + if (method() == nullptr) { + // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). + return; + } + bool printed_header = false; + for (int i = 0; i < OptEvent_Count; i++) { + int count = _optimization_counters[i]; + if (!printed_header) { + printed_header = true; + ResourceMark rm; + const char* holder_name = ""; + const char* method_name = ""; + const char* method_signature = ""; + int current_entry_bci = this->entry_bci(); + if (method() != nullptr) { + ciInstanceKlass* holder = method()->holder(); + if (holder != nullptr) { + holder_name = holder->name()->as_klass_external_name(); + } + method_name = method()->name()->as_utf8(); + method_signature = method()->signature()->as_symbol()->as_utf8(); + current_entry_bci = this->entry_bci(); + } else if (_stub_name != nullptr) { + method_name = _stub_name; + current_entry_bci = InvocationEntryBci; + } + const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; + tty->print_cr("OPTS_START"); + tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + } + tty->print_cr("%s=%d", optimization_event_names[i], count); + } + tty->print_cr("OPTS_END"); + } } //------------------------------Compile---------------------------------------- @@ -962,6 +1027,10 @@ Compile::Compile(ciEnv* ci_env, _allowed_reasons(0) { C = this; +#ifndef PRODUCT + Copy::zero_to_bytes(_optimization_counters, sizeof(_optimization_counters)); +#endif + // try to reuse an existing stub { BlobId blob_id = StubInfo::blob(_stub_id); @@ -1008,12 +1077,61 @@ Compile::Compile(ciEnv* ci_env, NOT_PRODUCT( verify_graph_edges(); ) Code_Gen(); + + if (TraceC2Optimizations) { + if (method() == nullptr) { + // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). + return; + } + bool printed_header = false; + for (int i = 0; i < OptEvent_Count; i++) { + int count = _optimization_counters[i]; + if (!printed_header) { + printed_header = true; + ResourceMark rm; + const char* holder_name = ""; + const char* method_name = ""; + const char* method_signature = ""; + int current_entry_bci = this->entry_bci(); + if (method() != nullptr) { + ciInstanceKlass* holder = method()->holder(); + if (holder != nullptr) { + holder_name = holder->name()->as_klass_external_name(); + } + method_name = method()->name()->as_utf8(); + method_signature = method()->signature()->as_symbol()->as_utf8(); + current_entry_bci = this->entry_bci(); + } else if (_stub_name != nullptr) { + method_name = _stub_name; + current_entry_bci = InvocationEntryBci; + } + const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; + tty->print_cr("OPTS_START"); + tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + } + tty->print_cr("%s=%d", optimization_event_names[i], count); + } + tty->print_cr("OPTS_END"); + } + + // Insert printing here (see below) } Compile::~Compile() { delete _first_failure_details; }; + +#ifndef PRODUCT +void Compile::record_optimization_event(OptimizationEvent event) { + if (!TraceC2Optimizations) { + return; + } + assert((int)event >= 0 && (int)event < OptEvent_Count, "optimization event out of bounds"); + _optimization_counters[event]++; +} +#endif + //------------------------------Init------------------------------------------- // Prepare for a single compilation void Compile::Init(bool aliasing) { @@ -1932,7 +2050,7 @@ void Compile::process_for_merge_stores_igvn(PhaseIterGVN& igvn) { igvn.optimize(); if (failing()) return; assert(_for_merge_stores_igvn.length() == 0, "no more delayed nodes allowed"); - print_method(PHASE_AFTER_MERGE_STORES, 3); + print_method(PHASE_AFTER_MERGE_STORES, 3); // INTERESTING } } @@ -4827,6 +4945,7 @@ void Compile::add_coarsened_locks(GrowableArray& locks) { } } _coarsened_locks.append(locks_list); + record_optimization_event(OptEvent_LockCoarsening); } } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 2cada9c04c94d..23b4f627df8db 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -110,6 +110,29 @@ enum LoopOptsMode { LoopOptsVerify }; +enum OptimizationEvent { + OptEvent_LoopUnrolling = 0, + OptEvent_LoopPeeling, + OptEvent_ParallelInductionVars, + OptEvent_SplitIf, + OptEvent_LoopUnswitching, + OptEvent_ConditionalExpressionElimination, + OptEvent_FunctionInlining, + OptEvent_Deoptimization, + OptEvent_EscapeAnalysis, + OptEvent_EliminateLocks, + OptEvent_LockCoarsening, + OptEvent_ConditionalConstantPropagation, + OptEvent_EliminateAutobox, + OptEvent_BlockElimination, + OptEvent_SimplifyPhiFunction, + OptEvent_Canonicalization, + OptEvent_NullCheckElimination, + OptEvent_RangeCheckElimination, + OptEvent_OptimizePtrCompare, + OptEvent_Count +}; + // The type of all node counts and indexes. // It must hold at least 16 bits, but must also be fast to load and store. // This type, if less than 32 bits, could limit the number of possible nodes. @@ -382,6 +405,9 @@ class Compile : public Phase { GrowableArray _unstable_if_traps; // List of ifnodes after IGVN GrowableArray _coarsened_locks; // List of coarsened Lock and Unlock nodes ConnectionGraph* _congraph; +#ifndef PRODUCT + int _optimization_counters[OptEvent_Count]; +#endif #ifndef PRODUCT IdealGraphPrinter* _igv_printer; static IdealGraphPrinter* _debug_file_printer; @@ -879,6 +905,9 @@ class Compile : public Phase { _recent_alloc_ctl = ctl; _recent_alloc_obj = obj; } + + void record_optimization_event(OptimizationEvent event) PRODUCT_RETURN; + void record_dead_node(uint idx) { if (_dead_node_list.test_set(idx)) return; _dead_node_count++; } From f03f6b9c705265e5fd7a85ca596ce8fdedddaa50 Mon Sep 17 00:00:00 2001 From: Oli Date: Sun, 19 Oct 2025 12:07:37 +0200 Subject: [PATCH 23/40] add guards for release build --- src/hotspot/share/opto/compile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 93bc01b29de58..c71cbb0bf2f40 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -920,6 +920,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // Now generate code Code_Gen(); +#ifndef PRODUCT if (TraceC2Optimizations) { if (method() == nullptr) { // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). @@ -955,6 +956,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } tty->print_cr("OPTS_END"); } +#endif } //------------------------------Compile---------------------------------------- @@ -1078,6 +1080,7 @@ Compile::Compile(ciEnv* ci_env, Code_Gen(); +#ifndef PRODUCT if (TraceC2Optimizations) { if (method() == nullptr) { // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). @@ -1113,6 +1116,7 @@ Compile::Compile(ciEnv* ci_env, } tty->print_cr("OPTS_END"); } +#endif // Insert printing here (see below) } From a8403f06bdb8a5c833e2802bc47c5ed14e533364 Mon Sep 17 00:00:00 2001 From: Oli Date: Mon, 20 Oct 2025 10:37:22 +0200 Subject: [PATCH 24/40] remove very simple optimizations behaviors and add new ones --- src/hotspot/share/opto/cfgnode.cpp | 9 --------- src/hotspot/share/opto/compile.cpp | 13 ++++++++++--- src/hotspot/share/opto/compile.hpp | 6 ++++-- src/hotspot/share/opto/loopPredicate.cpp | 5 +++++ src/hotspot/share/opto/loopnode.cpp | 3 +++ src/hotspot/share/opto/loopopts.cpp | 1 + src/hotspot/share/opto/phaseX.cpp | 4 ---- 7 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 6a3667f1f5736..580aebec46015 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2145,7 +2145,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2160,7 +2159,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) { // IGVN transformation - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { return nullptr; // Identity will return TOP @@ -2173,13 +2171,11 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2252,7 +2248,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2287,7 +2282,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. if( can_reshape ) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // We can't return top if we are in Parse phase - cut inputs only @@ -2295,7 +2289,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2500,7 +2493,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2534,7 +2526,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index c71cbb0bf2f40..4d38935d863aa 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -108,11 +108,13 @@ static const char* const optimization_event_names[OptEvent_Count] = { "Conditional Constant Propagation", "Eliminate Autobox", "Block Elimination", - "Simplify Phi Function", - "Canonicalization", "Null Check Elimination", "Range Check Elimination", - "Optimize Ptr Compare" + "Optimize Ptr Compare", + "Merge Stores", + "Loop Predication", + "Auto Vectorization", + "Partial Peeling" }; #endif @@ -2046,15 +2048,20 @@ void Compile::process_for_merge_stores_igvn(PhaseIterGVN& igvn) { C->set_merge_stores_phase(); if (_for_merge_stores_igvn.length() > 0) { + bool performed_merge = false; while (_for_merge_stores_igvn.length() > 0) { Node* n = _for_merge_stores_igvn.pop(); n->remove_flag(Node::NodeFlags::Flag_for_merge_stores_igvn); igvn._worklist.push(n); + performed_merge = true; } igvn.optimize(); if (failing()) return; assert(_for_merge_stores_igvn.length() == 0, "no more delayed nodes allowed"); print_method(PHASE_AFTER_MERGE_STORES, 3); // INTERESTING + if (performed_merge) { + record_optimization_event(OptEvent_MergeStores); + } } } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 23b4f627df8db..aaa408c228745 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -125,11 +125,13 @@ enum OptimizationEvent { OptEvent_ConditionalConstantPropagation, OptEvent_EliminateAutobox, OptEvent_BlockElimination, - OptEvent_SimplifyPhiFunction, - OptEvent_Canonicalization, OptEvent_NullCheckElimination, OptEvent_RangeCheckElimination, OptEvent_OptimizePtrCompare, + OptEvent_MergeStores, + OptEvent_LoopPredication, + OptEvent_AutoVectorization, + OptEvent_PartialPeeling, OptEvent_Count }; diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 561f3ce75cb3a..fbc607b9c5d3a 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -27,6 +27,7 @@ #include "opto/callnode.hpp" #include "opto/castnode.hpp" #include "opto/connode.hpp" +#include "opto/compile.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" #include "opto/matcher.hpp" @@ -1316,6 +1317,10 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree* loop) { head->verify_strip_mined(1); + if (hoisted) { + C->record_optimization_event(OptEvent_LoopPredication); + } + return hoisted; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index a35ebaabdf1cd..1efcddbbbef26 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -5269,6 +5269,9 @@ void PhaseIdealLoop::build_and_optimize() { for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { IdealLoopTree* lpt = iter.current(); AutoVectorizeStatus status = auto_vectorize(lpt, vshared); + if (status == AutoVectorizeStatus::Success) { + C->record_optimization_event(OptEvent_AutoVectorization); + } if (status == AutoVectorizeStatus::TriedAndFailed) { // We tried vectorization, but failed. From now on only unroll the loop. diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 55348865cbcfe..fdb2db338e8b0 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4155,6 +4155,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { #endif C->print_method(PHASE_AFTER_PARTIAL_PEELING, 4, new_head_clone); + C->record_optimization_event(OptEvent_PartialPeeling); return true; } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c61d243c55283..446a8d39aa51c 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2170,7 +2170,6 @@ Node *PhaseIterGVN::transform_old(Node* n) { Node* con = makecon(t); // Make a constant add_users_to_worklist(k); subsume_node(k, con); // Everybody using k now uses con - C->record_optimization_event(OptEvent_Canonicalization); return con; } @@ -2180,7 +2179,6 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i - C->record_optimization_event(OptEvent_Canonicalization); return i; } @@ -2191,12 +2189,10 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i - C->record_optimization_event(OptEvent_Canonicalization); return i; } // Return Idealized original - C->record_optimization_event(OptEvent_Canonicalization); return k; } From a5f8fef324a30ef2479ef75da36657a114d0e3d4 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:08:04 +0200 Subject: [PATCH 25/40] instrumentation and optimization hooks for fuzzer --- src/hotspot/share/opto/block.cpp | 2 + src/hotspot/share/opto/callGenerator.cpp | 1 + src/hotspot/share/opto/cfgnode.cpp | 19 +++- src/hotspot/share/opto/compile.cpp | 121 ++++++++++++++++++++++- src/hotspot/share/opto/compile.hpp | 29 ++++++ src/hotspot/share/opto/doCall.cpp | 4 + src/hotspot/share/opto/escape.cpp | 2 + src/hotspot/share/opto/graphKit.cpp | 4 + src/hotspot/share/opto/ifnode.cpp | 7 +- src/hotspot/share/opto/loopTransform.cpp | 7 ++ src/hotspot/share/opto/loopUnswitch.cpp | 2 +- src/hotspot/share/opto/loopnode.cpp | 1 + src/hotspot/share/opto/loopopts.cpp | 1 + src/hotspot/share/opto/macro.cpp | 1 + src/hotspot/share/opto/phaseX.cpp | 1 + 15 files changed, 195 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 7d3d4ec16f4f1..db8c3315b5ff2 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -27,6 +27,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "opto/block.hpp" +#include "opto/compile.hpp" #include "opto/cfgnode.hpp" #include "opto/chaitin.hpp" #include "opto/loopnode.hpp" @@ -1017,6 +1018,7 @@ void PhaseCFG::remove_unreachable_blocks() { get_block(i)->_pre_order--; } _blocks.remove(dead->_pre_order); + C->record_optimization_event(OptEvent_BlockElimination); _number_of_blocks--; // Update the successors' predecessor list and push new unreachable blocks. for (uint i = 0; i < dead->_num_succs; i++) { diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 483cb73110341..7613c2c0fd4ef 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -707,6 +707,7 @@ void CallGenerator::do_late_inline_helper() { C->env()->notice_inlined_method(inline_cg()->method()); } C->set_inlining_progress(true); + C->record_optimization_event(OptEvent_FunctionInlining); C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup kit.replace_call(call, result, true, do_asserts); } diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 0293f42d79123..b686c7e4120db 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -30,6 +30,7 @@ #include "opto/addnode.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" @@ -2144,6 +2145,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2157,10 +2159,12 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { uin = unique_input(phase, true); } if (uin == top) { // Simplest case: no alive inputs. - if (can_reshape) // IGVN transformation + if (can_reshape) { // IGVN transformation + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; - else + } else { return nullptr; // Identity will return TOP + } } else if (uin != nullptr) { // Only one not-null unique input path is left. // Determine if this input is backedge of a loop. @@ -2169,11 +2173,13 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2246,6 +2252,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2279,13 +2286,16 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt != nullptr ) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. - if( can_reshape ) + if( can_reshape ) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; + } // We can't return top if we are in Parse phase - cut inputs only // to stop further optimizations for this phi. Identity will return TOP. assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2490,6 +2500,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2523,6 +2534,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -3135,4 +3147,3 @@ void BlackholeNode::format(PhaseRegAlloc* ra, outputStream* st) const { st->cr(); } #endif - diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 6babc13e1b315..a6f961503ca26 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -24,6 +24,7 @@ #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" +#include "ci/ciInstanceKlass.hpp" #include "ci/ciReplay.hpp" #include "classfile/javaClasses.hpp" #include "code/aotCodeCache.hpp" @@ -80,6 +81,7 @@ #include "opto/type.hpp" #include "opto/vector.hpp" #include "opto/vectornode.hpp" +#include "opto/c2_globals.hpp" #include "runtime/globals_extension.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" @@ -90,6 +92,30 @@ #include "utilities/hashTable.hpp" #include "utilities/macros.hpp" +#ifndef PRODUCT +static const char* const optimization_event_names[OptEvent_Count] = { + "Loop Unrolling", + "Loop Peeling", + "Parallel Induction Variables", + "Split If", + "Loop Unswitching", + "Conditional Expression Elimination", + "Function Inlining", + "Deoptimization", + "Escape Analysis", + "Eliminate Locks", + "Locks Coarsening", + "Conditional Constant Propagation", + "Eliminate Autobox", + "Block Elimination", + "Simplify Phi Function", + "Canonicalization", + "Null Check Elimination", + "Range Check Elimination", + "Optimize Ptr Compare" +}; +#endif + // -------------------- Compile::mach_constant_base_node ----------------------- // Constant table base node singleton. MachConstantBaseNode* Compile::mach_constant_base_node() { @@ -707,6 +733,10 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, TraceTime t1("Total compilation time", &_t_totalCompilation, CITime, CITimeVerbose); TraceTime t2(nullptr, &_t_methodCompilation, CITime, false); +#ifndef PRODUCT + Copy::zero_to_bytes(_optimization_counters, sizeof(_optimization_counters)); +#endif + #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) bool print_opto_assembly = directive->PrintOptoAssemblyOption; // We can always print a disassembly, either abstract (hex dump) or @@ -891,6 +921,41 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // Now generate code Code_Gen(); + if (TraceC2Optimizations) { + if (method() == nullptr) { + // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). + return; + } + bool printed_header = false; + for (int i = 0; i < OptEvent_Count; i++) { + int count = _optimization_counters[i]; + if (!printed_header) { + printed_header = true; + ResourceMark rm; + const char* holder_name = ""; + const char* method_name = ""; + const char* method_signature = ""; + int current_entry_bci = this->entry_bci(); + if (method() != nullptr) { + ciInstanceKlass* holder = method()->holder(); + if (holder != nullptr) { + holder_name = holder->name()->as_klass_external_name(); + } + method_name = method()->name()->as_utf8(); + method_signature = method()->signature()->as_symbol()->as_utf8(); + current_entry_bci = this->entry_bci(); + } else if (_stub_name != nullptr) { + method_name = _stub_name; + current_entry_bci = InvocationEntryBci; + } + const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; + tty->print_cr("OPTS_START"); + tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + } + tty->print_cr("%s=%d", optimization_event_names[i], count); + } + tty->print_cr("OPTS_END"); + } } //------------------------------Compile---------------------------------------- @@ -964,6 +1029,10 @@ Compile::Compile(ciEnv* ci_env, _allowed_reasons(0) { C = this; +#ifndef PRODUCT + Copy::zero_to_bytes(_optimization_counters, sizeof(_optimization_counters)); +#endif + // try to reuse an existing stub { BlobId blob_id = StubInfo::blob(_stub_id); @@ -1010,12 +1079,61 @@ Compile::Compile(ciEnv* ci_env, NOT_PRODUCT( verify_graph_edges(); ) Code_Gen(); + + if (TraceC2Optimizations) { + if (method() == nullptr) { + // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). + return; + } + bool printed_header = false; + for (int i = 0; i < OptEvent_Count; i++) { + int count = _optimization_counters[i]; + if (!printed_header) { + printed_header = true; + ResourceMark rm; + const char* holder_name = ""; + const char* method_name = ""; + const char* method_signature = ""; + int current_entry_bci = this->entry_bci(); + if (method() != nullptr) { + ciInstanceKlass* holder = method()->holder(); + if (holder != nullptr) { + holder_name = holder->name()->as_klass_external_name(); + } + method_name = method()->name()->as_utf8(); + method_signature = method()->signature()->as_symbol()->as_utf8(); + current_entry_bci = this->entry_bci(); + } else if (_stub_name != nullptr) { + method_name = _stub_name; + current_entry_bci = InvocationEntryBci; + } + const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; + tty->print_cr("OPTS_START"); + tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + } + tty->print_cr("%s=%d", optimization_event_names[i], count); + } + tty->print_cr("OPTS_END"); + } + + // Insert printing here (see below) } Compile::~Compile() { delete _first_failure_details; }; + +#ifndef PRODUCT +void Compile::record_optimization_event(OptimizationEvent event) { + if (!TraceC2Optimizations) { + return; + } + assert((int)event >= 0 && (int)event < OptEvent_Count, "optimization event out of bounds"); + _optimization_counters[event]++; +} +#endif + //------------------------------Init------------------------------------------- // Prepare for a single compilation void Compile::Init(bool aliasing) { @@ -1934,7 +2052,7 @@ void Compile::process_for_merge_stores_igvn(PhaseIterGVN& igvn) { igvn.optimize(); if (failing()) return; assert(_for_merge_stores_igvn.length() == 0, "no more delayed nodes allowed"); - print_method(PHASE_AFTER_MERGE_STORES, 3); + print_method(PHASE_AFTER_MERGE_STORES, 3); // INTERESTING } } @@ -4835,6 +4953,7 @@ void Compile::add_coarsened_locks(GrowableArray& locks) { } } _coarsened_locks.append(locks_list); + record_optimization_event(OptEvent_LockCoarsening); } } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 66a5497a7ad16..942e96d90ee9c 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -110,6 +110,29 @@ enum LoopOptsMode { LoopOptsVerify }; +enum OptimizationEvent { + OptEvent_LoopUnrolling = 0, + OptEvent_LoopPeeling, + OptEvent_ParallelInductionVars, + OptEvent_SplitIf, + OptEvent_LoopUnswitching, + OptEvent_ConditionalExpressionElimination, + OptEvent_FunctionInlining, + OptEvent_Deoptimization, + OptEvent_EscapeAnalysis, + OptEvent_EliminateLocks, + OptEvent_LockCoarsening, + OptEvent_ConditionalConstantPropagation, + OptEvent_EliminateAutobox, + OptEvent_BlockElimination, + OptEvent_SimplifyPhiFunction, + OptEvent_Canonicalization, + OptEvent_NullCheckElimination, + OptEvent_RangeCheckElimination, + OptEvent_OptimizePtrCompare, + OptEvent_Count +}; + // The type of all node counts and indexes. // It must hold at least 16 bits, but must also be fast to load and store. // This type, if less than 32 bits, could limit the number of possible nodes. @@ -380,6 +403,9 @@ class Compile : public Phase { GrowableArray _unstable_if_traps; // List of ifnodes after IGVN GrowableArray _coarsened_locks; // List of coarsened Lock and Unlock nodes ConnectionGraph* _congraph; +#ifndef PRODUCT + int _optimization_counters[OptEvent_Count]; +#endif #ifndef PRODUCT IdealGraphPrinter* _igv_printer; static IdealGraphPrinter* _debug_file_printer; @@ -879,6 +905,9 @@ class Compile : public Phase { _recent_alloc_ctl = ctl; _recent_alloc_obj = obj; } + + void record_optimization_event(OptimizationEvent event) PRODUCT_RETURN; + void record_dead_node(uint idx) { if (_dead_node_list.test_set(idx)) return; _dead_node_count++; } diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index ad7b64f93f5a0..822260340c15c 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -37,6 +37,7 @@ #include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/mulnode.hpp" #include "opto/parse.hpp" #include "opto/rootnode.hpp" @@ -698,6 +699,9 @@ void Parse::do_call() { if (cg->is_inline()) { // Accumulate has_loops estimate C->env()->notice_inlined_method(cg->method()); + if (!cg->is_late_inline()) { + C->record_optimization_event(OptEvent_FunctionInlining); + } } // Reset parser state from [new_]jvms, which now carries results of the call. diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index a148b167ee301..e9eedf83cdbdd 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -118,6 +118,7 @@ void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) { if (congraph->compute_escape()) { // There are non escaping objects. C->set_congraph(congraph); + C->record_optimization_event(OptEvent_EscapeAnalysis); } // Cleanup. if (oop_null->outcnt() == 0) { @@ -3256,6 +3257,7 @@ void ConnectionGraph::optimize_ideal_graph(GrowableArray& ptr_cmp_worklis } } #endif + C->record_optimization_event(OptEvent_OptimizePtrCompare); igvn->replace_node(n, cmp); } } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index d4776d9d2f0bd..71361f2d2ff3f 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1309,6 +1309,7 @@ Node* GraphKit::null_check_common(Node* value, BasicType type, // If so, then the value is already null. if (t->higher_equal(TypePtr::NULL_PTR)) { NOT_PRODUCT(explicit_null_checks_elided++); + C->record_optimization_event(OptEvent_NullCheckElimination); return value; // Elided null assert quickly! } } else { @@ -1318,6 +1319,7 @@ Node* GraphKit::null_check_common(Node* value, BasicType type, if (t->meet(TypePtr::NULL_PTR) != t->remove_speculative()) { // same as: if (!TypePtr::NULL_PTR->higher_equal(t)) ... NOT_PRODUCT(explicit_null_checks_elided++); + C->record_optimization_event(OptEvent_NullCheckElimination); return value; // Elided null check quickly! } } @@ -2087,6 +2089,8 @@ Node* GraphKit::uncommon_trap(int trap_request, } if (stopped()) return nullptr; // trap reachable? + C->record_optimization_event(OptEvent_Deoptimization); + // Note: If ProfileTraps is true, and if a deopt. actually // occurs here, the runtime will make sure an MDO exists. There is // no need to call method()->ensure_method_data() at this point. diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 83e975b95a26e..4227687817020 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -29,6 +29,7 @@ #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/connode.hpp" +#include "opto/compile.hpp" #include "opto/loopnode.hpp" #include "opto/phaseX.hpp" #include "opto/predicates_enums.hpp" @@ -1794,7 +1795,8 @@ static int subsuming_bool_test_encode(Node* node) { Node* IfProjNode::Identity(PhaseGVN* phase) { // Can only optimize if cannot go the other way const TypeTuple *t = phase->type(in(0))->is_tuple(); - if (t == TypeTuple::IFNEITHER || (always_taken(t) && + bool taken = always_taken(t); + if (t == TypeTuple::IFNEITHER || (taken && // During parsing (GVN) we don't remove dead code aggressively. // Cut off dead branch and let PhaseRemoveUseless take care of it. (!phase->is_IterGVN() || @@ -1805,6 +1807,9 @@ Node* IfProjNode::Identity(PhaseGVN* phase) { // will cause this node to be reprocessed once the dead branch is killed. in(0)->outcnt() == 1))) { // IfNode control + if (taken) { + phase->C->record_optimization_event(OptEvent_ConditionalExpressionElimination); + } if (in(0)->is_BaseCountedLoopEnd()) { // CountedLoopEndNode may be eliminated by if subsuming, replace CountedLoopNode with LoopNode to // avoid mismatching between CountedLoopNode and CountedLoopEndNode in the following optimization. diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index f92833e9e1c0a..7e1f747dde20e 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -777,6 +777,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { C->set_major_progress(); + C->record_optimization_event(OptEvent_LoopPeeling); // Peeling a 'main' loop in a pre/main/post situation obfuscates the // 'pre' loop from the main and the 'pre' can no longer have its // iterations adjusted. Therefore, we need to declare this loop as @@ -2175,6 +2176,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj } #endif + C->record_optimization_event(OptEvent_LoopUnrolling); C->print_method(PHASE_AFTER_LOOP_UNROLLING, 4, clone_head); } @@ -2618,6 +2620,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { #endif assert(RangeCheckElimination, ""); + bool eliminated_range_check = false; CountedLoopNode *cl = loop->_head->as_CountedLoop(); // protect against stride not being a constant @@ -2791,6 +2794,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { // sense of the test. C->print_method(PHASE_BEFORE_RANGE_CHECK_ELIMINATION, 4, iff); + eliminated_range_check = true; // Perform the limit computations in jlong to avoid overflow jlong lscale_con = scale_con; @@ -2970,6 +2974,9 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { assert(is_dominator(new_limit_ctrl, get_ctrl(iffm->in(1)->in(1))), "control of cmp should be below control of updated input"); C->print_method(PHASE_AFTER_RANGE_CHECK_ELIMINATION, 4, cl); + if (eliminated_range_check) { + C->record_optimization_event(OptEvent_RangeCheckElimination); + } } // Adjust control for node and its inputs (and inputs of its inputs) to be above the pre end diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index f79afee31039c..11991b45c3015 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -405,6 +405,7 @@ void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) { increment_unswitch_counts(original_head, new_head); NOT_PRODUCT(trace_loop_unswitching_result(unswitched_loop_selector, original_head, new_head);) + C->record_optimization_event(OptEvent_LoopUnswitching); C->print_method(PHASE_AFTER_LOOP_UNSWITCHING, 4, new_head); C->set_major_progress(); } @@ -688,4 +689,3 @@ void PhaseIdealLoop::increment_unswitch_counts(LoopNode* original_head, LoopNode original_head->set_unswitch_count(unswitch_count); new_head->set_unswitch_count(unswitch_count); } - diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index e8058edb4e5e5..c4810141c78d1 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4441,6 +4441,7 @@ void PhaseIdealLoop::replace_parallel_iv(IdealLoopTree *loop) { set_ctrl(add, cl); _igvn.replace_node( phi2, add ); + C->record_optimization_event(OptEvent_ParallelInductionVars); // Sometimes an induction variable is unused if (add->outcnt() == 0) { _igvn.remove_dead_node(add); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index ae7b318ece491..9fc5d8cbd015f 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1472,6 +1472,7 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { tty->print_cr("Split-If"); } do_split_if(iff); + C->record_optimization_event(OptEvent_SplitIf); C->print_method(PHASE_AFTER_SPLIT_IF, 4, iff); return; } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 88cb59c115fc2..f533065bbcf1d 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2126,6 +2126,7 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { #endif alock->log_lock_optimization(C, "eliminate_lock"); + C->record_optimization_event(OptEvent_EliminateLocks); #ifndef PRODUCT if (PrintEliminateLocks) { diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index ce5160c5984aa..65b797e0233b5 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -3182,6 +3182,7 @@ Node *PhaseCCP::transform_once( Node *n ) { if( !n->is_Con() ) { if( t != Type::TOP ) { nn = makecon(t); // ConNode::make(t); + C->record_optimization_event(OptEvent_ConditionalConstantPropagation); NOT_PRODUCT( inc_constants(); ) } else if( n->is_Region() ) { // Unreachable region // Note: nn == C->top() From e0a0e6391ca09e2df9fc6f46b254f84ec8bdb35d Mon Sep 17 00:00:00 2001 From: Oli Date: Sun, 19 Oct 2025 12:07:37 +0200 Subject: [PATCH 26/40] add guards for release build --- src/hotspot/share/opto/compile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index a6f961503ca26..fa78e80611c3e 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -921,6 +921,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // Now generate code Code_Gen(); +#ifndef PRODUCT if (TraceC2Optimizations) { if (method() == nullptr) { // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). @@ -956,6 +957,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } tty->print_cr("OPTS_END"); } +#endif } //------------------------------Compile---------------------------------------- @@ -1080,6 +1082,7 @@ Compile::Compile(ciEnv* ci_env, Code_Gen(); +#ifndef PRODUCT if (TraceC2Optimizations) { if (method() == nullptr) { // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). @@ -1115,6 +1118,7 @@ Compile::Compile(ciEnv* ci_env, } tty->print_cr("OPTS_END"); } +#endif // Insert printing here (see below) } From 76adf8b783b5f04cfa3cea8d4177d6592e58dc98 Mon Sep 17 00:00:00 2001 From: Oli Date: Mon, 20 Oct 2025 10:37:22 +0200 Subject: [PATCH 27/40] remove very simple optimizations behaviors and add new ones --- src/hotspot/share/opto/cfgnode.cpp | 9 --------- src/hotspot/share/opto/compile.cpp | 13 ++++++++++--- src/hotspot/share/opto/compile.hpp | 6 ++++-- src/hotspot/share/opto/loopPredicate.cpp | 5 +++++ src/hotspot/share/opto/loopnode.cpp | 3 +++ src/hotspot/share/opto/loopopts.cpp | 1 + 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index b686c7e4120db..617e3da47f4a1 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2145,7 +2145,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2160,7 +2159,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) { // IGVN transformation - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { return nullptr; // Identity will return TOP @@ -2173,13 +2171,11 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2252,7 +2248,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2287,7 +2282,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. if( can_reshape ) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // We can't return top if we are in Parse phase - cut inputs only @@ -2295,7 +2289,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2500,7 +2493,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2534,7 +2526,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index fa78e80611c3e..71f9d16d06845 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -108,11 +108,13 @@ static const char* const optimization_event_names[OptEvent_Count] = { "Conditional Constant Propagation", "Eliminate Autobox", "Block Elimination", - "Simplify Phi Function", - "Canonicalization", "Null Check Elimination", "Range Check Elimination", - "Optimize Ptr Compare" + "Optimize Ptr Compare", + "Merge Stores", + "Loop Predication", + "Auto Vectorization", + "Partial Peeling" }; #endif @@ -2048,15 +2050,20 @@ void Compile::process_for_merge_stores_igvn(PhaseIterGVN& igvn) { C->set_merge_stores_phase(); if (_for_merge_stores_igvn.length() > 0) { + bool performed_merge = false; while (_for_merge_stores_igvn.length() > 0) { Node* n = _for_merge_stores_igvn.pop(); n->remove_flag(Node::NodeFlags::Flag_for_merge_stores_igvn); igvn._worklist.push(n); + performed_merge = true; } igvn.optimize(); if (failing()) return; assert(_for_merge_stores_igvn.length() == 0, "no more delayed nodes allowed"); print_method(PHASE_AFTER_MERGE_STORES, 3); // INTERESTING + if (performed_merge) { + record_optimization_event(OptEvent_MergeStores); + } } } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 942e96d90ee9c..3bdfbcda3dc41 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -125,11 +125,13 @@ enum OptimizationEvent { OptEvent_ConditionalConstantPropagation, OptEvent_EliminateAutobox, OptEvent_BlockElimination, - OptEvent_SimplifyPhiFunction, - OptEvent_Canonicalization, OptEvent_NullCheckElimination, OptEvent_RangeCheckElimination, OptEvent_OptimizePtrCompare, + OptEvent_MergeStores, + OptEvent_LoopPredication, + OptEvent_AutoVectorization, + OptEvent_PartialPeeling, OptEvent_Count }; diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 561f3ce75cb3a..fbc607b9c5d3a 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -27,6 +27,7 @@ #include "opto/callnode.hpp" #include "opto/castnode.hpp" #include "opto/connode.hpp" +#include "opto/compile.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" #include "opto/matcher.hpp" @@ -1316,6 +1317,10 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree* loop) { head->verify_strip_mined(1); + if (hoisted) { + C->record_optimization_event(OptEvent_LoopPredication); + } + return hoisted; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index c4810141c78d1..47fae28cd138d 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -5274,6 +5274,9 @@ void PhaseIdealLoop::build_and_optimize() { for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { IdealLoopTree* lpt = iter.current(); AutoVectorizeStatus status = auto_vectorize(lpt, vshared); + if (status == AutoVectorizeStatus::Success) { + C->record_optimization_event(OptEvent_AutoVectorization); + } if (status == AutoVectorizeStatus::TriedAndFailed) { // We tried vectorization, but failed. From now on only unroll the loop. diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 9fc5d8cbd015f..e3529595cde30 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4166,6 +4166,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { #endif C->print_method(PHASE_AFTER_PARTIAL_PEELING, 4, new_head_clone); + C->record_optimization_event(OptEvent_PartialPeeling); return true; } From 30d0a3f7c9cb86d7a5c89ed62f573cf0ed83208e Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:08:04 +0200 Subject: [PATCH 28/40] instrumentation and optimization hooks for fuzzer --- src/hotspot/share/opto/block.cpp | 2 + src/hotspot/share/opto/callGenerator.cpp | 1 + src/hotspot/share/opto/cfgnode.cpp | 19 +++- src/hotspot/share/opto/compile.cpp | 121 ++++++++++++++++++++++- src/hotspot/share/opto/compile.hpp | 29 ++++++ src/hotspot/share/opto/doCall.cpp | 4 + src/hotspot/share/opto/escape.cpp | 2 + src/hotspot/share/opto/graphKit.cpp | 4 + src/hotspot/share/opto/ifnode.cpp | 7 +- src/hotspot/share/opto/loopTransform.cpp | 7 ++ src/hotspot/share/opto/loopUnswitch.cpp | 2 +- src/hotspot/share/opto/loopnode.cpp | 1 + src/hotspot/share/opto/loopopts.cpp | 1 + src/hotspot/share/opto/macro.cpp | 1 + src/hotspot/share/opto/phaseX.cpp | 1 + 15 files changed, 195 insertions(+), 7 deletions(-) diff --git a/src/hotspot/share/opto/block.cpp b/src/hotspot/share/opto/block.cpp index 7d3d4ec16f4f1..db8c3315b5ff2 100644 --- a/src/hotspot/share/opto/block.cpp +++ b/src/hotspot/share/opto/block.cpp @@ -27,6 +27,7 @@ #include "memory/allocation.inline.hpp" #include "memory/resourceArea.hpp" #include "opto/block.hpp" +#include "opto/compile.hpp" #include "opto/cfgnode.hpp" #include "opto/chaitin.hpp" #include "opto/loopnode.hpp" @@ -1017,6 +1018,7 @@ void PhaseCFG::remove_unreachable_blocks() { get_block(i)->_pre_order--; } _blocks.remove(dead->_pre_order); + C->record_optimization_event(OptEvent_BlockElimination); _number_of_blocks--; // Update the successors' predecessor list and push new unreachable blocks. for (uint i = 0; i < dead->_num_succs; i++) { diff --git a/src/hotspot/share/opto/callGenerator.cpp b/src/hotspot/share/opto/callGenerator.cpp index 483cb73110341..7613c2c0fd4ef 100644 --- a/src/hotspot/share/opto/callGenerator.cpp +++ b/src/hotspot/share/opto/callGenerator.cpp @@ -707,6 +707,7 @@ void CallGenerator::do_late_inline_helper() { C->env()->notice_inlined_method(inline_cg()->method()); } C->set_inlining_progress(true); + C->record_optimization_event(OptEvent_FunctionInlining); C->set_do_cleanup(kit.stopped()); // path is dead; needs cleanup kit.replace_call(call, result, true, do_asserts); } diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 0293f42d79123..b686c7e4120db 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -30,6 +30,7 @@ #include "opto/addnode.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/connode.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" @@ -2144,6 +2145,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2157,10 +2159,12 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { uin = unique_input(phase, true); } if (uin == top) { // Simplest case: no alive inputs. - if (can_reshape) // IGVN transformation + if (can_reshape) { // IGVN transformation + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; - else + } else { return nullptr; // Identity will return TOP + } } else if (uin != nullptr) { // Only one not-null unique input path is left. // Determine if this input is backedge of a loop. @@ -2169,11 +2173,13 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2246,6 +2252,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2279,13 +2286,16 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt != nullptr ) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. - if( can_reshape ) + if( can_reshape ) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; + } // We can't return top if we are in Parse phase - cut inputs only // to stop further optimizations for this phi. Identity will return TOP. assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2490,6 +2500,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2523,6 +2534,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -3135,4 +3147,3 @@ void BlackholeNode::format(PhaseRegAlloc* ra, outputStream* st) const { st->cr(); } #endif - diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 6babc13e1b315..a6f961503ca26 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -24,6 +24,7 @@ #include "asm/macroAssembler.hpp" #include "asm/macroAssembler.inline.hpp" +#include "ci/ciInstanceKlass.hpp" #include "ci/ciReplay.hpp" #include "classfile/javaClasses.hpp" #include "code/aotCodeCache.hpp" @@ -80,6 +81,7 @@ #include "opto/type.hpp" #include "opto/vector.hpp" #include "opto/vectornode.hpp" +#include "opto/c2_globals.hpp" #include "runtime/globals_extension.hpp" #include "runtime/sharedRuntime.hpp" #include "runtime/signature.hpp" @@ -90,6 +92,30 @@ #include "utilities/hashTable.hpp" #include "utilities/macros.hpp" +#ifndef PRODUCT +static const char* const optimization_event_names[OptEvent_Count] = { + "Loop Unrolling", + "Loop Peeling", + "Parallel Induction Variables", + "Split If", + "Loop Unswitching", + "Conditional Expression Elimination", + "Function Inlining", + "Deoptimization", + "Escape Analysis", + "Eliminate Locks", + "Locks Coarsening", + "Conditional Constant Propagation", + "Eliminate Autobox", + "Block Elimination", + "Simplify Phi Function", + "Canonicalization", + "Null Check Elimination", + "Range Check Elimination", + "Optimize Ptr Compare" +}; +#endif + // -------------------- Compile::mach_constant_base_node ----------------------- // Constant table base node singleton. MachConstantBaseNode* Compile::mach_constant_base_node() { @@ -707,6 +733,10 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, TraceTime t1("Total compilation time", &_t_totalCompilation, CITime, CITimeVerbose); TraceTime t2(nullptr, &_t_methodCompilation, CITime, false); +#ifndef PRODUCT + Copy::zero_to_bytes(_optimization_counters, sizeof(_optimization_counters)); +#endif + #if defined(SUPPORT_ASSEMBLY) || defined(SUPPORT_ABSTRACT_ASSEMBLY) bool print_opto_assembly = directive->PrintOptoAssemblyOption; // We can always print a disassembly, either abstract (hex dump) or @@ -891,6 +921,41 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // Now generate code Code_Gen(); + if (TraceC2Optimizations) { + if (method() == nullptr) { + // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). + return; + } + bool printed_header = false; + for (int i = 0; i < OptEvent_Count; i++) { + int count = _optimization_counters[i]; + if (!printed_header) { + printed_header = true; + ResourceMark rm; + const char* holder_name = ""; + const char* method_name = ""; + const char* method_signature = ""; + int current_entry_bci = this->entry_bci(); + if (method() != nullptr) { + ciInstanceKlass* holder = method()->holder(); + if (holder != nullptr) { + holder_name = holder->name()->as_klass_external_name(); + } + method_name = method()->name()->as_utf8(); + method_signature = method()->signature()->as_symbol()->as_utf8(); + current_entry_bci = this->entry_bci(); + } else if (_stub_name != nullptr) { + method_name = _stub_name; + current_entry_bci = InvocationEntryBci; + } + const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; + tty->print_cr("OPTS_START"); + tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + } + tty->print_cr("%s=%d", optimization_event_names[i], count); + } + tty->print_cr("OPTS_END"); + } } //------------------------------Compile---------------------------------------- @@ -964,6 +1029,10 @@ Compile::Compile(ciEnv* ci_env, _allowed_reasons(0) { C = this; +#ifndef PRODUCT + Copy::zero_to_bytes(_optimization_counters, sizeof(_optimization_counters)); +#endif + // try to reuse an existing stub { BlobId blob_id = StubInfo::blob(_stub_id); @@ -1010,12 +1079,61 @@ Compile::Compile(ciEnv* ci_env, NOT_PRODUCT( verify_graph_edges(); ) Code_Gen(); + + if (TraceC2Optimizations) { + if (method() == nullptr) { + // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). + return; + } + bool printed_header = false; + for (int i = 0; i < OptEvent_Count; i++) { + int count = _optimization_counters[i]; + if (!printed_header) { + printed_header = true; + ResourceMark rm; + const char* holder_name = ""; + const char* method_name = ""; + const char* method_signature = ""; + int current_entry_bci = this->entry_bci(); + if (method() != nullptr) { + ciInstanceKlass* holder = method()->holder(); + if (holder != nullptr) { + holder_name = holder->name()->as_klass_external_name(); + } + method_name = method()->name()->as_utf8(); + method_signature = method()->signature()->as_symbol()->as_utf8(); + current_entry_bci = this->entry_bci(); + } else if (_stub_name != nullptr) { + method_name = _stub_name; + current_entry_bci = InvocationEntryBci; + } + const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; + tty->print_cr("OPTS_START"); + tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + } + tty->print_cr("%s=%d", optimization_event_names[i], count); + } + tty->print_cr("OPTS_END"); + } + + // Insert printing here (see below) } Compile::~Compile() { delete _first_failure_details; }; + +#ifndef PRODUCT +void Compile::record_optimization_event(OptimizationEvent event) { + if (!TraceC2Optimizations) { + return; + } + assert((int)event >= 0 && (int)event < OptEvent_Count, "optimization event out of bounds"); + _optimization_counters[event]++; +} +#endif + //------------------------------Init------------------------------------------- // Prepare for a single compilation void Compile::Init(bool aliasing) { @@ -1934,7 +2052,7 @@ void Compile::process_for_merge_stores_igvn(PhaseIterGVN& igvn) { igvn.optimize(); if (failing()) return; assert(_for_merge_stores_igvn.length() == 0, "no more delayed nodes allowed"); - print_method(PHASE_AFTER_MERGE_STORES, 3); + print_method(PHASE_AFTER_MERGE_STORES, 3); // INTERESTING } } @@ -4835,6 +4953,7 @@ void Compile::add_coarsened_locks(GrowableArray& locks) { } } _coarsened_locks.append(locks_list); + record_optimization_event(OptEvent_LockCoarsening); } } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 66a5497a7ad16..942e96d90ee9c 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -110,6 +110,29 @@ enum LoopOptsMode { LoopOptsVerify }; +enum OptimizationEvent { + OptEvent_LoopUnrolling = 0, + OptEvent_LoopPeeling, + OptEvent_ParallelInductionVars, + OptEvent_SplitIf, + OptEvent_LoopUnswitching, + OptEvent_ConditionalExpressionElimination, + OptEvent_FunctionInlining, + OptEvent_Deoptimization, + OptEvent_EscapeAnalysis, + OptEvent_EliminateLocks, + OptEvent_LockCoarsening, + OptEvent_ConditionalConstantPropagation, + OptEvent_EliminateAutobox, + OptEvent_BlockElimination, + OptEvent_SimplifyPhiFunction, + OptEvent_Canonicalization, + OptEvent_NullCheckElimination, + OptEvent_RangeCheckElimination, + OptEvent_OptimizePtrCompare, + OptEvent_Count +}; + // The type of all node counts and indexes. // It must hold at least 16 bits, but must also be fast to load and store. // This type, if less than 32 bits, could limit the number of possible nodes. @@ -380,6 +403,9 @@ class Compile : public Phase { GrowableArray _unstable_if_traps; // List of ifnodes after IGVN GrowableArray _coarsened_locks; // List of coarsened Lock and Unlock nodes ConnectionGraph* _congraph; +#ifndef PRODUCT + int _optimization_counters[OptEvent_Count]; +#endif #ifndef PRODUCT IdealGraphPrinter* _igv_printer; static IdealGraphPrinter* _debug_file_printer; @@ -879,6 +905,9 @@ class Compile : public Phase { _recent_alloc_ctl = ctl; _recent_alloc_obj = obj; } + + void record_optimization_event(OptimizationEvent event) PRODUCT_RETURN; + void record_dead_node(uint idx) { if (_dead_node_list.test_set(idx)) return; _dead_node_count++; } diff --git a/src/hotspot/share/opto/doCall.cpp b/src/hotspot/share/opto/doCall.cpp index ad7b64f93f5a0..822260340c15c 100644 --- a/src/hotspot/share/opto/doCall.cpp +++ b/src/hotspot/share/opto/doCall.cpp @@ -37,6 +37,7 @@ #include "opto/callGenerator.hpp" #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" +#include "opto/compile.hpp" #include "opto/mulnode.hpp" #include "opto/parse.hpp" #include "opto/rootnode.hpp" @@ -698,6 +699,9 @@ void Parse::do_call() { if (cg->is_inline()) { // Accumulate has_loops estimate C->env()->notice_inlined_method(cg->method()); + if (!cg->is_late_inline()) { + C->record_optimization_event(OptEvent_FunctionInlining); + } } // Reset parser state from [new_]jvms, which now carries results of the call. diff --git a/src/hotspot/share/opto/escape.cpp b/src/hotspot/share/opto/escape.cpp index a148b167ee301..e9eedf83cdbdd 100644 --- a/src/hotspot/share/opto/escape.cpp +++ b/src/hotspot/share/opto/escape.cpp @@ -118,6 +118,7 @@ void ConnectionGraph::do_analysis(Compile *C, PhaseIterGVN *igvn) { if (congraph->compute_escape()) { // There are non escaping objects. C->set_congraph(congraph); + C->record_optimization_event(OptEvent_EscapeAnalysis); } // Cleanup. if (oop_null->outcnt() == 0) { @@ -3256,6 +3257,7 @@ void ConnectionGraph::optimize_ideal_graph(GrowableArray& ptr_cmp_worklis } } #endif + C->record_optimization_event(OptEvent_OptimizePtrCompare); igvn->replace_node(n, cmp); } } diff --git a/src/hotspot/share/opto/graphKit.cpp b/src/hotspot/share/opto/graphKit.cpp index d4776d9d2f0bd..71361f2d2ff3f 100644 --- a/src/hotspot/share/opto/graphKit.cpp +++ b/src/hotspot/share/opto/graphKit.cpp @@ -1309,6 +1309,7 @@ Node* GraphKit::null_check_common(Node* value, BasicType type, // If so, then the value is already null. if (t->higher_equal(TypePtr::NULL_PTR)) { NOT_PRODUCT(explicit_null_checks_elided++); + C->record_optimization_event(OptEvent_NullCheckElimination); return value; // Elided null assert quickly! } } else { @@ -1318,6 +1319,7 @@ Node* GraphKit::null_check_common(Node* value, BasicType type, if (t->meet(TypePtr::NULL_PTR) != t->remove_speculative()) { // same as: if (!TypePtr::NULL_PTR->higher_equal(t)) ... NOT_PRODUCT(explicit_null_checks_elided++); + C->record_optimization_event(OptEvent_NullCheckElimination); return value; // Elided null check quickly! } } @@ -2087,6 +2089,8 @@ Node* GraphKit::uncommon_trap(int trap_request, } if (stopped()) return nullptr; // trap reachable? + C->record_optimization_event(OptEvent_Deoptimization); + // Note: If ProfileTraps is true, and if a deopt. actually // occurs here, the runtime will make sure an MDO exists. There is // no need to call method()->ensure_method_data() at this point. diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 83e975b95a26e..4227687817020 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -29,6 +29,7 @@ #include "opto/castnode.hpp" #include "opto/cfgnode.hpp" #include "opto/connode.hpp" +#include "opto/compile.hpp" #include "opto/loopnode.hpp" #include "opto/phaseX.hpp" #include "opto/predicates_enums.hpp" @@ -1794,7 +1795,8 @@ static int subsuming_bool_test_encode(Node* node) { Node* IfProjNode::Identity(PhaseGVN* phase) { // Can only optimize if cannot go the other way const TypeTuple *t = phase->type(in(0))->is_tuple(); - if (t == TypeTuple::IFNEITHER || (always_taken(t) && + bool taken = always_taken(t); + if (t == TypeTuple::IFNEITHER || (taken && // During parsing (GVN) we don't remove dead code aggressively. // Cut off dead branch and let PhaseRemoveUseless take care of it. (!phase->is_IterGVN() || @@ -1805,6 +1807,9 @@ Node* IfProjNode::Identity(PhaseGVN* phase) { // will cause this node to be reprocessed once the dead branch is killed. in(0)->outcnt() == 1))) { // IfNode control + if (taken) { + phase->C->record_optimization_event(OptEvent_ConditionalExpressionElimination); + } if (in(0)->is_BaseCountedLoopEnd()) { // CountedLoopEndNode may be eliminated by if subsuming, replace CountedLoopNode with LoopNode to // avoid mismatching between CountedLoopNode and CountedLoopEndNode in the following optimization. diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index f92833e9e1c0a..7e1f747dde20e 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -777,6 +777,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne void PhaseIdealLoop::do_peeling(IdealLoopTree *loop, Node_List &old_new) { C->set_major_progress(); + C->record_optimization_event(OptEvent_LoopPeeling); // Peeling a 'main' loop in a pre/main/post situation obfuscates the // 'pre' loop from the main and the 'pre' can no longer have its // iterations adjusted. Therefore, we need to declare this loop as @@ -2175,6 +2176,7 @@ void PhaseIdealLoop::do_unroll(IdealLoopTree *loop, Node_List &old_new, bool adj } #endif + C->record_optimization_event(OptEvent_LoopUnrolling); C->print_method(PHASE_AFTER_LOOP_UNROLLING, 4, clone_head); } @@ -2618,6 +2620,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { #endif assert(RangeCheckElimination, ""); + bool eliminated_range_check = false; CountedLoopNode *cl = loop->_head->as_CountedLoop(); // protect against stride not being a constant @@ -2791,6 +2794,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { // sense of the test. C->print_method(PHASE_BEFORE_RANGE_CHECK_ELIMINATION, 4, iff); + eliminated_range_check = true; // Perform the limit computations in jlong to avoid overflow jlong lscale_con = scale_con; @@ -2970,6 +2974,9 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree* loop) { assert(is_dominator(new_limit_ctrl, get_ctrl(iffm->in(1)->in(1))), "control of cmp should be below control of updated input"); C->print_method(PHASE_AFTER_RANGE_CHECK_ELIMINATION, 4, cl); + if (eliminated_range_check) { + C->record_optimization_event(OptEvent_RangeCheckElimination); + } } // Adjust control for node and its inputs (and inputs of its inputs) to be above the pre end diff --git a/src/hotspot/share/opto/loopUnswitch.cpp b/src/hotspot/share/opto/loopUnswitch.cpp index f79afee31039c..11991b45c3015 100644 --- a/src/hotspot/share/opto/loopUnswitch.cpp +++ b/src/hotspot/share/opto/loopUnswitch.cpp @@ -405,6 +405,7 @@ void PhaseIdealLoop::do_unswitching(IdealLoopTree* loop, Node_List& old_new) { increment_unswitch_counts(original_head, new_head); NOT_PRODUCT(trace_loop_unswitching_result(unswitched_loop_selector, original_head, new_head);) + C->record_optimization_event(OptEvent_LoopUnswitching); C->print_method(PHASE_AFTER_LOOP_UNSWITCHING, 4, new_head); C->set_major_progress(); } @@ -688,4 +689,3 @@ void PhaseIdealLoop::increment_unswitch_counts(LoopNode* original_head, LoopNode original_head->set_unswitch_count(unswitch_count); new_head->set_unswitch_count(unswitch_count); } - diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index e8058edb4e5e5..c4810141c78d1 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -4441,6 +4441,7 @@ void PhaseIdealLoop::replace_parallel_iv(IdealLoopTree *loop) { set_ctrl(add, cl); _igvn.replace_node( phi2, add ); + C->record_optimization_event(OptEvent_ParallelInductionVars); // Sometimes an induction variable is unused if (add->outcnt() == 0) { _igvn.remove_dead_node(add); diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index ae7b318ece491..9fc5d8cbd015f 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -1472,6 +1472,7 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) { tty->print_cr("Split-If"); } do_split_if(iff); + C->record_optimization_event(OptEvent_SplitIf); C->print_method(PHASE_AFTER_SPLIT_IF, 4, iff); return; } diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 88cb59c115fc2..f533065bbcf1d 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2126,6 +2126,7 @@ bool PhaseMacroExpand::eliminate_locking_node(AbstractLockNode *alock) { #endif alock->log_lock_optimization(C, "eliminate_lock"); + C->record_optimization_event(OptEvent_EliminateLocks); #ifndef PRODUCT if (PrintEliminateLocks) { diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index ce5160c5984aa..65b797e0233b5 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -3182,6 +3182,7 @@ Node *PhaseCCP::transform_once( Node *n ) { if( !n->is_Con() ) { if( t != Type::TOP ) { nn = makecon(t); // ConNode::make(t); + C->record_optimization_event(OptEvent_ConditionalConstantPropagation); NOT_PRODUCT( inc_constants(); ) } else if( n->is_Region() ) { // Unreachable region // Note: nn == C->top() From 6b8652d1e8562f3b003e8acac680393b60490fd3 Mon Sep 17 00:00:00 2001 From: Oli Date: Sun, 19 Oct 2025 12:07:37 +0200 Subject: [PATCH 29/40] add guards for release build --- src/hotspot/share/opto/compile.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index a6f961503ca26..fa78e80611c3e 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -921,6 +921,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, // Now generate code Code_Gen(); +#ifndef PRODUCT if (TraceC2Optimizations) { if (method() == nullptr) { // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). @@ -956,6 +957,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, } tty->print_cr("OPTS_END"); } +#endif } //------------------------------Compile---------------------------------------- @@ -1080,6 +1082,7 @@ Compile::Compile(ciEnv* ci_env, Code_Gen(); +#ifndef PRODUCT if (TraceC2Optimizations) { if (method() == nullptr) { // Skip C2 runtime stubs (notify_jvmti_vthread_mount_blob, …). @@ -1115,6 +1118,7 @@ Compile::Compile(ciEnv* ci_env, } tty->print_cr("OPTS_END"); } +#endif // Insert printing here (see below) } From cd8ee2c0d8aaac14b1abf27aa6fa6afc5b0bf32e Mon Sep 17 00:00:00 2001 From: Oli Date: Mon, 20 Oct 2025 10:37:22 +0200 Subject: [PATCH 30/40] remove very simple optimizations behaviors and add new ones --- src/hotspot/share/opto/cfgnode.cpp | 9 --------- src/hotspot/share/opto/compile.cpp | 13 ++++++++++--- src/hotspot/share/opto/compile.hpp | 6 ++++-- src/hotspot/share/opto/loopPredicate.cpp | 5 +++++ src/hotspot/share/opto/loopnode.cpp | 3 +++ src/hotspot/share/opto/loopopts.cpp | 1 + 6 files changed, 23 insertions(+), 14 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index b686c7e4120db..617e3da47f4a1 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2145,7 +2145,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2160,7 +2159,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) { // IGVN transformation - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { return nullptr; // Identity will return TOP @@ -2173,13 +2171,11 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2252,7 +2248,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2287,7 +2282,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. if( can_reshape ) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // We can't return top if we are in Parse phase - cut inputs only @@ -2295,7 +2289,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2500,7 +2493,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2534,7 +2526,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index fa78e80611c3e..71f9d16d06845 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -108,11 +108,13 @@ static const char* const optimization_event_names[OptEvent_Count] = { "Conditional Constant Propagation", "Eliminate Autobox", "Block Elimination", - "Simplify Phi Function", - "Canonicalization", "Null Check Elimination", "Range Check Elimination", - "Optimize Ptr Compare" + "Optimize Ptr Compare", + "Merge Stores", + "Loop Predication", + "Auto Vectorization", + "Partial Peeling" }; #endif @@ -2048,15 +2050,20 @@ void Compile::process_for_merge_stores_igvn(PhaseIterGVN& igvn) { C->set_merge_stores_phase(); if (_for_merge_stores_igvn.length() > 0) { + bool performed_merge = false; while (_for_merge_stores_igvn.length() > 0) { Node* n = _for_merge_stores_igvn.pop(); n->remove_flag(Node::NodeFlags::Flag_for_merge_stores_igvn); igvn._worklist.push(n); + performed_merge = true; } igvn.optimize(); if (failing()) return; assert(_for_merge_stores_igvn.length() == 0, "no more delayed nodes allowed"); print_method(PHASE_AFTER_MERGE_STORES, 3); // INTERESTING + if (performed_merge) { + record_optimization_event(OptEvent_MergeStores); + } } } diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 942e96d90ee9c..3bdfbcda3dc41 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -125,11 +125,13 @@ enum OptimizationEvent { OptEvent_ConditionalConstantPropagation, OptEvent_EliminateAutobox, OptEvent_BlockElimination, - OptEvent_SimplifyPhiFunction, - OptEvent_Canonicalization, OptEvent_NullCheckElimination, OptEvent_RangeCheckElimination, OptEvent_OptimizePtrCompare, + OptEvent_MergeStores, + OptEvent_LoopPredication, + OptEvent_AutoVectorization, + OptEvent_PartialPeeling, OptEvent_Count }; diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 561f3ce75cb3a..fbc607b9c5d3a 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -27,6 +27,7 @@ #include "opto/callnode.hpp" #include "opto/castnode.hpp" #include "opto/connode.hpp" +#include "opto/compile.hpp" #include "opto/convertnode.hpp" #include "opto/loopnode.hpp" #include "opto/matcher.hpp" @@ -1316,6 +1317,10 @@ bool PhaseIdealLoop::loop_predication_impl(IdealLoopTree* loop) { head->verify_strip_mined(1); + if (hoisted) { + C->record_optimization_event(OptEvent_LoopPredication); + } + return hoisted; } diff --git a/src/hotspot/share/opto/loopnode.cpp b/src/hotspot/share/opto/loopnode.cpp index c4810141c78d1..47fae28cd138d 100644 --- a/src/hotspot/share/opto/loopnode.cpp +++ b/src/hotspot/share/opto/loopnode.cpp @@ -5274,6 +5274,9 @@ void PhaseIdealLoop::build_and_optimize() { for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) { IdealLoopTree* lpt = iter.current(); AutoVectorizeStatus status = auto_vectorize(lpt, vshared); + if (status == AutoVectorizeStatus::Success) { + C->record_optimization_event(OptEvent_AutoVectorization); + } if (status == AutoVectorizeStatus::TriedAndFailed) { // We tried vectorization, but failed. From now on only unroll the loop. diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index 9fc5d8cbd015f..e3529595cde30 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -4166,6 +4166,7 @@ bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { #endif C->print_method(PHASE_AFTER_PARTIAL_PEELING, 4, new_head_clone); + C->record_optimization_event(OptEvent_PartialPeeling); return true; } From 9dcce5c36bb1b8ec90ba354b2b21ea87848ab28b Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 20:59:29 +0200 Subject: [PATCH 31/40] add flag for C2 Optimization Tracing (debug only) --- src/hotspot/share/opto/c2_globals.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index 0a4f231c49b36..62d7a2b3fff57 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -659,6 +659,9 @@ product(bool, TraceTypeProfile, false, DIAGNOSTIC, \ "Trace type profile") \ \ + develop(bool, TraceC2Optimizations, false, \ + "Trace selected C2 loop optimizations (counts only)") \ + \ develop(bool, PoisonOSREntry, true, \ "Detect abnormal calls to OSR code") \ \ From 9f21aabe62fba7844928cd181a805015a0024bd0 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:00:37 +0200 Subject: [PATCH 32/40] add canonicalization hook --- src/hotspot/share/opto/phaseX.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 65b797e0233b5..61be585d2c234 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2171,6 +2171,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { Node* con = makecon(t); // Make a constant add_users_to_worklist(k); subsume_node(k, con); // Everybody using k now uses con + C->record_optimization_event(OptEvent_Canonicalization); return con; } @@ -2180,6 +2181,7 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i + C->record_optimization_event(OptEvent_Canonicalization); return i; } @@ -2190,10 +2192,12 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i + C->record_optimization_event(OptEvent_Canonicalization); return i; } // Return Idealized original + C->record_optimization_event(OptEvent_Canonicalization); return k; } From a359e36b7effd70c3decd17d6e97c1f797036535 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:01:10 +0200 Subject: [PATCH 33/40] eliminate autobox hooks --- src/hotspot/share/opto/macro.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index f533065bbcf1d..144676b029cdb 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -2423,6 +2423,9 @@ void PhaseMacroExpand::eliminate_macro_nodes() { break; case Node::Class_CallStaticJava: success = eliminate_boxing_node(n->as_CallStaticJava()); + if (success) { + C->record_optimization_event(OptEvent_EliminateAutobox); + } break; case Node::Class_Lock: case Node::Class_Unlock: From 6e66840c325c8c16afcf6dd31bff66b4e50d9302 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:01:25 +0200 Subject: [PATCH 34/40] Eliminate Autobox hook --- src/hotspot/share/opto/macro.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/hotspot/share/opto/macro.cpp b/src/hotspot/share/opto/macro.cpp index 144676b029cdb..dcd53d4c1afae 100644 --- a/src/hotspot/share/opto/macro.cpp +++ b/src/hotspot/share/opto/macro.cpp @@ -1099,6 +1099,9 @@ bool PhaseMacroExpand::eliminate_allocate_node(AllocateNode *alloc) { } process_users_of_allocation(alloc); + if (boxing_alloc) { + C->record_optimization_event(OptEvent_EliminateAutobox); + } #ifndef PRODUCT if (PrintEliminateAllocations) { From a314ae074b109d79b302e7baf32d8ccc5f4680e1 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 18 Oct 2025 21:05:35 +0200 Subject: [PATCH 35/40] simplify phi hook --- src/hotspot/share/opto/cfgnode.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index 617e3da47f4a1..b686c7e4120db 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2145,6 +2145,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2159,6 +2160,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) { // IGVN transformation + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { return nullptr; // Identity will return TOP @@ -2171,11 +2173,13 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2248,6 +2252,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2282,6 +2287,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. if( can_reshape ) { + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // We can't return top if we are in Parse phase - cut inputs only @@ -2289,6 +2295,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2493,6 +2500,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2526,6 +2534,7 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi + phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } From 07ec2ad4eb7ebdf682160a4a83457fe23a2d46d2 Mon Sep 17 00:00:00 2001 From: Oli Date: Mon, 20 Oct 2025 10:37:22 +0200 Subject: [PATCH 36/40] remove very simple optimizations behaviors and add new ones --- src/hotspot/share/opto/cfgnode.cpp | 9 --------- src/hotspot/share/opto/phaseX.cpp | 4 ---- 2 files changed, 13 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index b686c7e4120db..617e3da47f4a1 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -2145,7 +2145,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (can_reshape && outcnt() == 0) { // set_req() above may kill outputs if Phi is referenced // only by itself on the dead (top) control path. - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } @@ -2160,7 +2159,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (uin == top) { // Simplest case: no alive inputs. if (can_reshape) { // IGVN transformation - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { return nullptr; // Identity will return TOP @@ -2173,13 +2171,11 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if (is_data_loop(r->as_Region(), uin, phase)) { // Break this data loop to avoid creation of a dead loop. if (can_reshape) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } else { // We can't return top if we are in Parse phase - cut inputs only // let Identity to handle the case. replace_edge(uin, top, phase); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } } @@ -2252,7 +2248,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { #endif // Identity may not return the expected uin, if it has to wait for the region, in irreducible case assert(ident == uin || ident->is_top() || must_wait_for_region_in_irreducible_loop(phase), "Identity must clean this up"); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } @@ -2287,7 +2282,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { if( opt == unsafe_id || is_unsafe_data_reference(opt) ) { // Found dead loop. if( can_reshape ) { - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // We can't return top if we are in Parse phase - cut inputs only @@ -2295,7 +2289,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { assert(req() == 3, "only diamond merge phi here"); set_req(1, top); set_req(2, top); - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return nullptr; } else { return opt; @@ -2500,7 +2493,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { // Equivalent code is in MemNode::Ideal_common Node *m = phase->transform(n); if (outcnt() == 0) { // Above transform() may kill us! - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } // If transformed to a MergeMem, get the desired slice @@ -2534,7 +2526,6 @@ Node *PhiNode::Ideal(PhaseGVN *phase, bool can_reshape) { } if (!saw_safe_input) { // There is a dead loop: All inputs are either dead or reference back to this phi - phase->C->record_optimization_event(OptEvent_SimplifyPhiFunction); return top; } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 61be585d2c234..65b797e0233b5 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -2171,7 +2171,6 @@ Node *PhaseIterGVN::transform_old(Node* n) { Node* con = makecon(t); // Make a constant add_users_to_worklist(k); subsume_node(k, con); // Everybody using k now uses con - C->record_optimization_event(OptEvent_Canonicalization); return con; } @@ -2181,7 +2180,6 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i - C->record_optimization_event(OptEvent_Canonicalization); return i; } @@ -2192,12 +2190,10 @@ Node *PhaseIterGVN::transform_old(Node* n) { NOT_PRODUCT(set_progress();) add_users_to_worklist(k); subsume_node(k, i); // Everybody using k now uses i - C->record_optimization_event(OptEvent_Canonicalization); return i; } // Return Idealized original - C->record_optimization_event(OptEvent_Canonicalization); return k; } From c0baf092b043995c885eb4c8ad9a0e6ecb76fea0 Mon Sep 17 00:00:00 2001 From: Oli Date: Mon, 27 Oct 2025 17:35:16 +0100 Subject: [PATCH 37/40] remove trailing whitespace --- src/hotspot/share/opto/compile.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 3bdfbcda3dc41..12ce2bf14db34 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -908,7 +908,7 @@ class Compile : public Phase { _recent_alloc_obj = obj; } - void record_optimization_event(OptimizationEvent event) PRODUCT_RETURN; + void record_optimization_event(OptimizationEvent event) PRODUCT_RETURN; void record_dead_node(uint idx) { if (_dead_node_list.test_set(idx)) return; _dead_node_count++; From 159a8a143647aa3cd7a31d8e321562339662b957 Mon Sep 17 00:00:00 2001 From: Oli Date: Sat, 1 Nov 2025 13:18:39 +0100 Subject: [PATCH 38/40] first try to track additional opt behaviors --- src/hotspot/share/opto/compile.cpp | 7 ++- src/hotspot/share/opto/compile.hpp | 5 ++ src/hotspot/share/opto/loopTransform.cpp | 72 +++++++++++++++++++++++- src/hotspot/share/opto/phaseX.cpp | 4 ++ 4 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index 71f9d16d06845..c8a27bd8081f5 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -114,7 +114,12 @@ static const char* const optimization_event_names[OptEvent_Count] = { "Merge Stores", "Loop Predication", "Auto Vectorization", - "Partial Peeling" + "Partial Peeling", + "Iterative GVN Iterations", + "Loop Iteration Split", + "Reassociate Invariants", + "Loop Intrinsification", + "Peephole" }; #endif diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 12ce2bf14db34..34ef42d36fc3d 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -132,6 +132,11 @@ enum OptimizationEvent { OptEvent_LoopPredication, OptEvent_AutoVectorization, OptEvent_PartialPeeling, + OptEvent_IterGVNIteration, + OptEvent_LoopIterationSplit, + OptEvent_ReassociateInvariants, + OptEvent_LoopIntrinsification, + OptEvent_Peephole, OptEvent_Count }; diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index 7e1f747dde20e..fc533f0740e7d 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -499,14 +499,25 @@ Node* IdealLoopTree::reassociate(Node* n1, PhaseIdealLoop *phase) { //---------------------reassociate_invariants----------------------------- // Reassociate invariant expressions: void IdealLoopTree::reassociate_invariants(PhaseIdealLoop *phase) { +#ifndef PRODUCT + bool changed = false; +#endif for (int i = _body.size() - 1; i >= 0; i--) { Node *n = _body.at(i); for (int j = 0; j < 5; j++) { Node* nn = reassociate(n, phase); if (nn == nullptr) break; +#ifndef PRODUCT + changed = true; +#endif n = nn; // again } } +#ifndef PRODUCT + if (changed) { + phase->C->record_optimization_event(OptEvent_ReassociateInvariants); + } +#endif } //------------------------------policy_peeling--------------------------------- @@ -3466,6 +3477,11 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (!_head->is_CountedLoop()) { // Non-counted loop if (PartialPeelLoop) { bool rc = phase->partial_peel(this, old_new); +#ifndef PRODUCT + if (rc) { + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); + } +#endif if (Compile::current()->failing()) { return false; } if (rc) { // Partial peel succeeded so terminate this round of loop opts @@ -3475,13 +3491,26 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (policy_peeling(phase)) { // Should we peel? if (PrintOpto) { tty->print_cr("should_peel"); } phase->do_peeling(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif } else if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return false; // need to recalculate idom data } else if (phase->duplicate_loop_backedge(this, old_new)) { +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return false; } else if (_head->is_LongCountedLoop()) { - phase->create_loop_nest(this, old_new); + if (phase->create_loop_nest(this, old_new)) { +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif + } } return true; } @@ -3517,15 +3546,24 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (cl->is_normal_loop()) { if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return false; // need to recalculate idom data } if (policy_maximally_unroll(phase)) { // Here we did some unrolling and peeling. Eventually we will // completely unroll this loop and it will no longer be a loop. phase->do_maximally_unroll(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return true; } if (StressDuplicateBackedge && phase->duplicate_loop_backedge(this, old_new)) { +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return false; } } @@ -3562,6 +3600,9 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (should_rce || should_unroll) { if (cl->is_normal_loop()) { // Convert to 'pre/main/post' loops if (should_rce_long && phase->create_loop_nest(this, old_new)) { +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return true; } uint estimate = est_loop_clone_sz(3); @@ -3580,12 +3621,18 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n } phase->insert_pre_post_loops(this, old_new, peel_only); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif } // Adjust the pre- and main-loop limits to let the pre and post loops run // with full checks, but the main-loop with no checks. Remove said checks // from the main body. if (should_rce) { phase->do_range_check(this); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif } // Double loop body for unrolling. Adjust the minimum-trip test (will do @@ -3596,17 +3643,30 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (should_unroll && !should_peel) { if (SuperWordLoopUnrollAnalysis) { phase->insert_vector_post_loop(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif } phase->do_unroll(this, old_new, true); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif } } else { // Else we have an unchanged counted loop if (should_peel) { // Might want to peel but do nothing else if (phase->may_require_nodes(est_peeling)) { phase->do_peeling(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif } } if (should_rce_long) { - phase->create_loop_nest(this, old_new); + if (phase->create_loop_nest(this, old_new)) { +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif + } } } return true; @@ -3640,6 +3700,9 @@ bool IdealLoopTree::iteration_split(PhaseIdealLoop* phase, Node_List &old_new) { AutoNodeBudget node_budget(phase); if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); +#ifndef PRODUCT + phase->C->record_optimization_event(OptEvent_LoopIterationSplit); +#endif return false; // need to recalculate idom data } } @@ -3661,6 +3724,11 @@ bool PhaseIdealLoop::do_intrinsify_fill() { IdealLoopTree* lpt = iter.current(); changed |= intrinsify_fill(lpt); } +#ifndef PRODUCT + if (changed) { + C->record_optimization_event(OptEvent_LoopIntrinsification); + } +#endif return changed; } diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index 65b797e0233b5..863671f085e49 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -1065,6 +1065,9 @@ void PhaseIterGVN::optimize() { remove_dead_node(n); } loop_count++; +#ifndef PRODUCT + C->record_optimization_event(OptEvent_IterGVNIteration); +#endif } NOT_PRODUCT(verify_PhaseIterGVN();) C->print_method(PHASE_AFTER_ITER_GVN, 3); @@ -3321,6 +3324,7 @@ void PhasePeephole::do_transform() { tty->print_cr("peephole number: %d", result); } inc_peepholes(); + C->record_optimization_event(OptEvent_Peephole); #endif // Set progress, start again progress = true; From 2661e8aa4659b1b03d1cbd1957d87ee5fd8028df Mon Sep 17 00:00:00 2001 From: Oli Date: Mon, 3 Nov 2025 15:30:41 +0100 Subject: [PATCH 39/40] fix overcounting for LoopIterationSplit event --- src/hotspot/share/opto/loopTransform.cpp | 38 ------------------------ 1 file changed, 38 deletions(-) diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index fc533f0740e7d..87a11300da695 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -3477,11 +3477,6 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (!_head->is_CountedLoop()) { // Non-counted loop if (PartialPeelLoop) { bool rc = phase->partial_peel(this, old_new); -#ifndef PRODUCT - if (rc) { - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); - } -#endif if (Compile::current()->failing()) { return false; } if (rc) { // Partial peel succeeded so terminate this round of loop opts @@ -3491,19 +3486,10 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (policy_peeling(phase)) { // Should we peel? if (PrintOpto) { tty->print_cr("should_peel"); } phase->do_peeling(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif } else if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif return false; // need to recalculate idom data } else if (phase->duplicate_loop_backedge(this, old_new)) { -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif return false; } else if (_head->is_LongCountedLoop()) { if (phase->create_loop_nest(this, old_new)) { @@ -3546,24 +3532,15 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (cl->is_normal_loop()) { if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif return false; // need to recalculate idom data } if (policy_maximally_unroll(phase)) { // Here we did some unrolling and peeling. Eventually we will // completely unroll this loop and it will no longer be a loop. phase->do_maximally_unroll(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif return true; } if (StressDuplicateBackedge && phase->duplicate_loop_backedge(this, old_new)) { -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif return false; } } @@ -3630,9 +3607,6 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n // from the main body. if (should_rce) { phase->do_range_check(this); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif } // Double loop body for unrolling. Adjust the minimum-trip test (will do @@ -3643,22 +3617,13 @@ bool IdealLoopTree::iteration_split_impl(PhaseIdealLoop *phase, Node_List &old_n if (should_unroll && !should_peel) { if (SuperWordLoopUnrollAnalysis) { phase->insert_vector_post_loop(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif } phase->do_unroll(this, old_new, true); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif } } else { // Else we have an unchanged counted loop if (should_peel) { // Might want to peel but do nothing else if (phase->may_require_nodes(est_peeling)) { phase->do_peeling(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif } } if (should_rce_long) { @@ -3700,9 +3665,6 @@ bool IdealLoopTree::iteration_split(PhaseIdealLoop* phase, Node_List &old_new) { AutoNodeBudget node_budget(phase); if (policy_unswitching(phase)) { phase->do_unswitching(this, old_new); -#ifndef PRODUCT - phase->C->record_optimization_event(OptEvent_LoopIterationSplit); -#endif return false; // need to recalculate idom data } } From 2cf80f21a9d0c0de7d57e5f984b473f2e95fd528 Mon Sep 17 00:00:00 2001 From: Oli Date: Fri, 7 Nov 2025 11:22:38 +0100 Subject: [PATCH 40/40] aquire ttylocker before printing to prevent opt statistics interleaving --- src/hotspot/share/opto/compile.cpp | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index c8a27bd8081f5..598c56aa7c397 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -935,6 +935,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, return; } bool printed_header = false; + stringStream ss; for (int i = 0; i < OptEvent_Count; i++) { int count = _optimization_counters[i]; if (!printed_header) { @@ -957,12 +958,16 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci, current_entry_bci = InvocationEntryBci; } const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; - tty->print_cr("OPTS_START"); - tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + ss.print_cr("OPTS_START"); + ss.print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); } - tty->print_cr("%s=%d", optimization_event_names[i], count); + ss.print_cr("%s=%d", optimization_event_names[i], count); + } + if (printed_header) { + ss.print_cr("OPTS_END"); + ttyLocker ttyl; + tty->print_raw(ss.base()); } - tty->print_cr("OPTS_END"); } #endif } @@ -1096,6 +1101,7 @@ Compile::Compile(ciEnv* ci_env, return; } bool printed_header = false; + stringStream ss; for (int i = 0; i < OptEvent_Count; i++) { int count = _optimization_counters[i]; if (!printed_header) { @@ -1118,12 +1124,16 @@ Compile::Compile(ciEnv* ci_env, current_entry_bci = InvocationEntryBci; } const char* compilation_kind = is_osr_compilation() ? "OSR" : "non-OSR"; - tty->print_cr("OPTS_START"); - tty->print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); + ss.print_cr("OPTS_START"); + ss.print_cr("Opts|%s|%s|%s|%s|%d|%d", holder_name, method_name, method_signature, compilation_kind, current_entry_bci, _compile_id); } - tty->print_cr("%s=%d", optimization_event_names[i], count); + ss.print_cr("%s=%d", optimization_event_names[i], count); + } + if (printed_header) { + ss.print_cr("OPTS_END"); + ttyLocker ttyl; + tty->print_raw(ss.base()); } - tty->print_cr("OPTS_END"); } #endif