From a5f401f3a8534a64cf3c27c2ef67f17860de6d6b Mon Sep 17 00:00:00 2001 From: Christian Hagedorn Date: Wed, 26 Jun 2024 07:09:50 +0000 Subject: [PATCH] 8334650: Add debug information about whether an Assertion Predicate is for the init or last value Reviewed-by: roland, kvn --- src/hotspot/share/opto/cfgnode.hpp | 37 ++++++++++++++------- src/hotspot/share/opto/ifnode.cpp | 42 +++++++++++++++++++++--- src/hotspot/share/opto/loopPredicate.cpp | 15 ++++++--- src/hotspot/share/opto/loopTransform.cpp | 17 +++++----- src/hotspot/share/opto/loopnode.hpp | 13 ++++---- src/hotspot/share/opto/predicates.hpp | 9 +++++ 6 files changed, 97 insertions(+), 36 deletions(-) diff --git a/src/hotspot/share/opto/cfgnode.hpp b/src/hotspot/share/opto/cfgnode.hpp index de37f4cdd65..32ac8c0162f 100644 --- a/src/hotspot/share/opto/cfgnode.hpp +++ b/src/hotspot/share/opto/cfgnode.hpp @@ -57,6 +57,7 @@ class JProjNode; class JumpProjNode; class SCMemProjNode; class PhaseIdealLoop; +enum class AssertionPredicateType; // The success projection of a Parse Predicate is always an IfTrueNode and the uncommon projection an IfFalseNode typedef IfTrueNode ParsePredicateSuccessProj; @@ -318,11 +319,23 @@ public: //------------------------------IfNode----------------------------------------- // Output selected Control, based on a boolean test class IfNode : public MultiBranchNode { + public: + float _prob; // Probability of true path being taken. + float _fcnt; // Frequency counter + + private: + NOT_PRODUCT(AssertionPredicateType _assertion_predicate_type;) + + void init_node(Node* control, Node* bol) { + init_class_id(Class_If); + init_req(0, control); + init_req(1, bol); + } + // Size is bigger to hold the probability field. However, _prob does not // change the semantics so it does not appear in the hash & cmp functions. virtual uint size_of() const { return sizeof(*this); } -private: // Helper methods for fold_compares bool cmpi_folds(PhaseIterGVN* igvn, bool fold_ne = false); bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn); @@ -413,14 +426,8 @@ public: // Magic manifest probabilities such as 0.83, 0.7, ... can be found in // gen_subtype_check() and catch_inline_exceptions(). - float _prob; // Probability of true path being taken. - float _fcnt; // Frequency counter - IfNode( Node *control, Node *b, float p, float fcnt ) - : MultiBranchNode(2), _prob(p), _fcnt(fcnt) { - init_class_id(Class_If); - init_req(0,control); - init_req(1,b); - } + IfNode(Node* control, Node* bol, float p, float fcnt); + NOT_PRODUCT(IfNode(Node* control, Node* bol, float p, float fcnt, AssertionPredicateType assertion_predicate_type);) static IfNode* make_with_same_profile(IfNode* if_node_profile, Node* ctrl, BoolNode* bol); @@ -450,14 +457,20 @@ public: class RangeCheckNode : public IfNode { private: - int is_range_check(Node* &range, Node* &index, jint &offset); + int is_range_check(Node*& range, Node*& index, jint& offset); public: - RangeCheckNode(Node* control, Node *b, float p, float fcnt) - : IfNode(control, b, p, fcnt) { + RangeCheckNode(Node* control, Node* bol, float p, float fcnt) : IfNode(control, bol, p, fcnt) { init_class_id(Class_RangeCheck); } +#ifndef PRODUCT + RangeCheckNode(Node* control, Node* bol, float p, float fcnt, AssertionPredicateType assertion_predicate_type) + : IfNode(control, bol, p, fcnt, assertion_predicate_type) { + init_class_id(Class_RangeCheck); + } +#endif // NOT PRODUCT + virtual int Opcode() const; virtual Node* Ideal(PhaseGVN *phase, bool can_reshape); }; diff --git a/src/hotspot/share/opto/ifnode.cpp b/src/hotspot/share/opto/ifnode.cpp index 9760be95ddd..a5722c45a5e 100644 --- a/src/hotspot/share/opto/ifnode.cpp +++ b/src/hotspot/share/opto/ifnode.cpp @@ -47,6 +47,24 @@ extern uint explicit_null_checks_elided; #endif +IfNode::IfNode(Node* control, Node* bol, float p, float fcnt) + : MultiBranchNode(2), + _prob(p), + _fcnt(fcnt) + NOT_PRODUCT(COMMA _assertion_predicate_type(AssertionPredicateType::None)) { + init_node(control, bol); +} + +#ifndef PRODUCT +IfNode::IfNode(Node* control, Node* bol, float p, float fcnt, AssertionPredicateType assertion_predicate_type) + : MultiBranchNode(2), + _prob(p), + _fcnt(fcnt), + _assertion_predicate_type(assertion_predicate_type) { + init_node(control, bol); +} +#endif // NOT_PRODUCT + //============================================================================= //------------------------------Value------------------------------------------ // Return a tuple for whichever arm of the IF is reachable @@ -1822,11 +1840,23 @@ void IfProjNode::pin_array_access_nodes(PhaseIterGVN* igvn) { } #ifndef PRODUCT -//------------------------------dump_spec-------------------------------------- -void IfNode::dump_spec(outputStream *st) const { - st->print("P=%f, C=%f",_prob,_fcnt); +void IfNode::dump_spec(outputStream* st) const { + switch (_assertion_predicate_type) { + case AssertionPredicateType::Init_value: + st->print("#Init Value Assertion Predicate "); + break; + case AssertionPredicateType::Last_value: + st->print("#Last Value Assertion Predicate "); + break; + case AssertionPredicateType::None: + // No Assertion Predicate + break; + default: + fatal("Unknown Assertion Predicate type"); + } + st->print("P=%f, C=%f", _prob, _fcnt); } -#endif +#endif // NOT PRODUCT //------------------------------idealize_test---------------------------------- // Try to canonicalize tests better. Peek at the Cmp/Bool/If sequence and @@ -2181,6 +2211,8 @@ void ParsePredicateNode::dump_spec(outputStream* st) const { default: fatal("unknown kind"); } + if (_useless) { + st->print("#useless "); + } } - #endif // NOT PRODUCT diff --git a/src/hotspot/share/opto/loopPredicate.cpp b/src/hotspot/share/opto/loopPredicate.cpp index 998d3a27178..f8d3fa0b6d9 100644 --- a/src/hotspot/share/opto/loopPredicate.cpp +++ b/src/hotspot/share/opto/loopPredicate.cpp @@ -101,7 +101,8 @@ void PhaseIdealLoop::register_control(Node* n, IdealLoopTree *loop, Node* pred, // is an IfTrue projection. This code is also used to clone predicates to cloned loops. IfTrueNode* PhaseIdealLoop::create_new_if_for_predicate(ParsePredicateSuccessProj* parse_predicate_success_proj, Node* new_entry, const Deoptimization::DeoptReason reason, - const int opcode, const bool rewire_uncommon_proj_phi_inputs) { + const int opcode, const bool rewire_uncommon_proj_phi_inputs + NOT_PRODUCT (COMMA AssertionPredicateType assertion_predicate_type)) { assert(parse_predicate_success_proj->is_uncommon_trap_if_pattern(reason), "must be a uct if pattern!"); ParsePredicateNode* parse_predicate = parse_predicate_success_proj->in(0)->as_ParsePredicate(); ParsePredicateUncommonProj* uncommon_proj = parse_predicate->uncommon_proj(); @@ -143,10 +144,12 @@ IfTrueNode* PhaseIdealLoop::create_new_if_for_predicate(ParsePredicateSuccessPro IfNode* new_iff = nullptr; switch (opcode) { case Op_If: - new_iff = new IfNode(entry, parse_predicate->in(1), parse_predicate->_prob, parse_predicate->_fcnt); + new_iff = new IfNode(entry, parse_predicate->in(1), parse_predicate->_prob, parse_predicate->_fcnt + NOT_PRODUCT(COMMA assertion_predicate_type)); break; case Op_RangeCheck: - new_iff = new RangeCheckNode(entry, parse_predicate->in(1), parse_predicate->_prob, parse_predicate->_fcnt); + new_iff = new RangeCheckNode(entry, parse_predicate->in(1), parse_predicate->_prob, parse_predicate->_fcnt + NOT_PRODUCT(COMMA assertion_predicate_type)); break; case Op_ParsePredicate: new_iff = new ParsePredicateNode(entry, reason, &_igvn); @@ -1320,7 +1323,8 @@ IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealL Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); // This will go away once loop opts are over C->add_template_assertion_predicate_opaq(opaque_bol); register_new_node(opaque_bol, upper_bound_proj); - IfTrueNode* new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode()); + IfTrueNode* new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode(), + false NOT_PRODUCT(COMMA AssertionPredicateType::Init_value)); _igvn.replace_input_of(new_proj->in(0), 1, opaque_bol); assert(opaque_init->outcnt() > 0, "should be used"); @@ -1345,7 +1349,8 @@ IfTrueNode* PhaseIdealLoop::add_template_assertion_predicate(IfNode* iff, IdealL opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1)); C->add_template_assertion_predicate_opaq(opaque_bol); register_new_node(opaque_bol, new_proj); - new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode()); + new_proj = create_new_if_for_predicate(parse_predicate_proj, nullptr, reason, overflow ? Op_If : iff->Opcode(), + false NOT_PRODUCT(COMMA AssertionPredicateType::Last_value)); _igvn.replace_input_of(new_proj->in(0), 1, opaque_bol); assert(max_value->outcnt() > 0, "should be used"); assert(assertion_predicate_has_loop_opaque_node(new_proj->in(0)->as_If()), "unexpected"); diff --git a/src/hotspot/share/opto/loopTransform.cpp b/src/hotspot/share/opto/loopTransform.cpp index a02690778a0..1b29d31ba68 100644 --- a/src/hotspot/share/opto/loopTransform.cpp +++ b/src/hotspot/share/opto/loopTransform.cpp @@ -2769,10 +2769,9 @@ bool PhaseIdealLoop::is_scaled_iv_plus_extra_offset(Node* exp1, Node* offset3, N // Same as PhaseIdealLoop::duplicate_predicates() but for range checks // eliminated by iteration splitting. -Node* PhaseIdealLoop::add_range_check_elimination_assertion_predicate(IdealLoopTree* loop, Node* ctrl, - const int scale_con, Node* offset, Node* limit, - jint stride_con, Node* value, - const bool is_template) { +Node* PhaseIdealLoop::add_range_check_elimination_assertion_predicate( + IdealLoopTree* loop, Node* ctrl, const int scale_con, Node* offset, Node* limit, jint stride_con, Node* value, + const bool is_template NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type)) { bool overflow = false; BoolNode* bol = rc_predicate(ctrl, scale_con, offset, value, nullptr, stride_con, limit, (stride_con > 0) != (scale_con > 0), overflow); @@ -2993,8 +2992,9 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // Add two Template Assertion Predicates to create new Initialized Assertion Predicates from when either // unrolling or splitting this main-loop further. - loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset, - int_limit, stride_con, opaque_init, true); + loop_entry = add_range_check_elimination_assertion_predicate( + loop, loop_entry, scale_con, int_offset, int_limit, stride_con, opaque_init, true + NOT_PRODUCT(COMMA AssertionPredicateType::Init_value)); assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected"); Node* opaque_stride = new OpaqueLoopStrideNode(C, cl->stride()); @@ -3006,8 +3006,9 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) { // init + (current stride - initial stride) is within the loop so narrow its type by leveraging the type of the iv Phi max_value = new CastIINode(max_value, loop->_head->as_CountedLoop()->phi()->bottom_type()); register_new_node(max_value, loop_entry); - loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset, - int_limit, stride_con, max_value, true); + loop_entry = add_range_check_elimination_assertion_predicate( + loop, loop_entry, scale_con, int_offset, int_limit, stride_con, max_value, true + NOT_PRODUCT(COMMA AssertionPredicateType::Last_value)); assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected"); } else { diff --git a/src/hotspot/share/opto/loopnode.hpp b/src/hotspot/share/opto/loopnode.hpp index 17145c825a4..79faf9c931d 100644 --- a/src/hotspot/share/opto/loopnode.hpp +++ b/src/hotspot/share/opto/loopnode.hpp @@ -1339,9 +1339,10 @@ public: bool* p_short_scale, int depth); // Create a new if above the uncommon_trap_if_pattern for the predicate to be promoted - IfTrueNode* create_new_if_for_predicate(ParsePredicateSuccessProj* parse_predicate_proj, Node* new_entry, - Deoptimization::DeoptReason reason, int opcode, - bool rewire_uncommon_proj_phi_inputs = false); + IfTrueNode* create_new_if_for_predicate( + ParsePredicateSuccessProj* parse_predicate_proj, Node* new_entry, Deoptimization::DeoptReason reason, int opcode, + bool rewire_uncommon_proj_phi_inputs = false + NOT_PRODUCT (COMMA AssertionPredicateType assertion_predicate_type = AssertionPredicateType::None)); private: // Helper functions for create_new_if_for_predicate() @@ -1382,9 +1383,9 @@ public: IfProjNode* upper_bound_proj, int scale, Node* offset, Node* init, Node* limit, jint stride, Node* rng, bool& overflow, Deoptimization::DeoptReason reason); void eliminate_hoisted_range_check(IfTrueNode* hoisted_check_proj, IfTrueNode* template_assertion_predicate_proj); - Node* add_range_check_elimination_assertion_predicate(IdealLoopTree* loop, Node* predicate_proj, int scale_con, - Node* offset, Node* limit, int stride_con, Node* value, - bool is_template); + Node* add_range_check_elimination_assertion_predicate( + IdealLoopTree* loop, Node* predicate_proj, int scale_con, Node* offset, Node* limit, int stride_con, Node* value, + bool is_template NOT_PRODUCT(COMMA AssertionPredicateType assertion_predicate_type = AssertionPredicateType::None)); // Helper function to collect predicate for eliminating the useless ones void eliminate_useless_predicates(); diff --git a/src/hotspot/share/opto/predicates.hpp b/src/hotspot/share/opto/predicates.hpp index 9cac98eb993..08aa64f03e5 100644 --- a/src/hotspot/share/opto/predicates.hpp +++ b/src/hotspot/share/opto/predicates.hpp @@ -193,6 +193,15 @@ * Main Loop Head */ +#ifndef PRODUCT +// Assertion Predicates are either emitted to check the initial value of a range check in the first iteration or the last +// value of a range check in the last iteration of a loop. +enum class AssertionPredicateType { + None, // Not an Assertion Predicate + Init_value, + Last_value +}; +#endif // NOT PRODUCT // Class to represent Assertion Predicates with a HaltNode instead of an UCT (i.e. either an Initialized Assertion // Predicate or a Template Assertion Predicate created after the initial one at Loop Predication).