Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 18 additions & 10 deletions src/hotspot/share/opto/cfgnode.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 1997, 2024, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
Expand Down Expand Up @@ -28,6 +28,7 @@
#include "opto/multnode.hpp"
#include "opto/node.hpp"
#include "opto/opcodes.hpp"
#include "opto/predicates_enums.hpp"
#include "opto/type.hpp"

// Portions of code courtesy of Clifford Click
Expand Down Expand Up @@ -58,10 +59,7 @@ 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;
typedef IfFalseNode ParsePredicateUncommonProj;
enum class PredicateState;

//------------------------------RegionNode-------------------------------------
// The class of RegionNodes, which can be mapped to basic blocks in the
Expand Down Expand Up @@ -486,7 +484,11 @@ class RangeCheckNode : public IfNode {
// More information about predicates can be found in loopPredicate.cpp.
class ParsePredicateNode : public IfNode {
Deoptimization::DeoptReason _deopt_reason;
bool _useless; // If the associated loop dies, this parse predicate becomes useless and can be cleaned up by Value().

// When a Parse Predicate loses its connection to a loop head, it will be marked useless by
// EliminateUselessPredicates and cleaned up by Value(). It can also become useless when cloning it to both loops
// during Loop Multiversioning - we no longer use the old version.
PredicateState _predicate_state;
public:
ParsePredicateNode(Node* control, Deoptimization::DeoptReason deopt_reason, PhaseGVN* gvn);
virtual int Opcode() const;
Expand All @@ -497,15 +499,21 @@ class ParsePredicateNode : public IfNode {
}

bool is_useless() const {
return _useless;
return _predicate_state == PredicateState::Useless;
}

void mark_useless(PhaseIterGVN& igvn);

void mark_maybe_useful() {
_predicate_state = PredicateState::MaybeUseful;
}

void mark_useless() {
_useless = true;
bool is_useful() const {
return _predicate_state == PredicateState::Useful;
}

void mark_useful() {
_useless = false;
_predicate_state = PredicateState::Useful;
}

// Return the uncommon trap If projection of this Parse Predicate.
Expand Down
11 changes: 6 additions & 5 deletions src/hotspot/share/opto/compile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
#include "opto/mulnode.hpp"
#include "opto/narrowptrnode.hpp"
#include "opto/node.hpp"
#include "opto/opaquenode.hpp"
#include "opto/opcodes.hpp"
#include "opto/output.hpp"
#include "opto/parse.hpp"
Expand Down Expand Up @@ -394,7 +395,7 @@ void Compile::remove_useless_node(Node* dead) {
remove_expensive_node(dead);
}
if (dead->is_OpaqueTemplateAssertionPredicate()) {
remove_template_assertion_predicate_opaq(dead);
remove_template_assertion_predicate_opaque(dead->as_OpaqueTemplateAssertionPredicate());
}
if (dead->is_ParsePredicate()) {
remove_parse_predicate(dead->as_ParsePredicate());
Expand Down Expand Up @@ -450,7 +451,8 @@ void Compile::disconnect_useless_nodes(Unique_Node_List& useful, Unique_Node_Lis

remove_useless_nodes(_macro_nodes, useful); // remove useless macro nodes
remove_useless_nodes(_parse_predicates, useful); // remove useless Parse Predicate nodes
remove_useless_nodes(_template_assertion_predicate_opaqs, useful); // remove useless Assertion Predicate opaque nodes
// Remove useless Template Assertion Predicate opaque nodes
remove_useless_nodes(_template_assertion_predicate_opaques, useful);
remove_useless_nodes(_expensive_nodes, useful); // remove useless expensive nodes
remove_useless_nodes(_for_post_loop_igvn, useful); // remove useless node recorded for post loop opts IGVN pass
remove_useless_unstable_if_traps(useful); // remove useless unstable_if traps
Expand Down Expand Up @@ -648,7 +650,7 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
_intrinsics(comp_arena(), 0, 0, nullptr),
_macro_nodes(comp_arena(), 8, 0, nullptr),
_parse_predicates(comp_arena(), 8, 0, nullptr),
_template_assertion_predicate_opaqs(comp_arena(), 8, 0, nullptr),
_template_assertion_predicate_opaques(comp_arena(), 8, 0, nullptr),
_expensive_nodes(comp_arena(), 8, 0, nullptr),
_for_post_loop_igvn(comp_arena(), 8, 0, nullptr),
_unstable_if_traps(comp_arena(), 8, 0, nullptr),
Expand Down Expand Up @@ -1824,8 +1826,7 @@ void Compile::mark_parse_predicate_nodes_useless(PhaseIterGVN& igvn) {
}
for (int i = 0; i < parse_predicate_count(); i++) {
ParsePredicateNode* parse_predicate = _parse_predicates.at(i);
parse_predicate->mark_useless();
igvn._worklist.push(parse_predicate);
parse_predicate->mark_useless(igvn);
}
_parse_predicates.clear();
}
Expand Down
31 changes: 18 additions & 13 deletions src/hotspot/share/opto/compile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ class Node_List;
class Node_Notes;
class NodeHash;
class NodeCloneInfo;
class OpaqueTemplateAssertionPredicateNode;
class OptoReg;
class ParsePredicateNode;
class PhaseCFG;
Expand Down Expand Up @@ -370,8 +371,9 @@ class Compile : public Phase {
GrowableArray<CallGenerator*> _intrinsics; // List of intrinsics.
GrowableArray<Node*> _macro_nodes; // List of nodes which need to be expanded before matching.
GrowableArray<ParsePredicateNode*> _parse_predicates; // List of Parse Predicates.
// List of OpaqueTemplateAssertionPredicateNode nodes for Template Assertion Predicates.
GrowableArray<Node*> _template_assertion_predicate_opaqs;
// List of OpaqueTemplateAssertionPredicateNode nodes for Template Assertion Predicates which can be seen as list
// of Template Assertion Predicates themselves.
GrowableArray<OpaqueTemplateAssertionPredicateNode*> _template_assertion_predicate_opaques;
GrowableArray<Node*> _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common
GrowableArray<Node*> _for_post_loop_igvn; // List of nodes for IGVN after loop opts are over
GrowableArray<UnstableIfTrap*> _unstable_if_traps; // List of ifnodes after IGVN
Expand Down Expand Up @@ -681,18 +683,21 @@ class Compile : public Phase {
static IdealGraphPrinter* debug_network_printer() { return _debug_network_printer; }
#endif

const GrowableArray<ParsePredicateNode*>& parse_predicates() const {
return _parse_predicates;
}

const GrowableArray<OpaqueTemplateAssertionPredicateNode*>& template_assertion_predicate_opaques() const {
return _template_assertion_predicate_opaques;
}

int macro_count() const { return _macro_nodes.length(); }
int parse_predicate_count() const { return _parse_predicates.length(); }
int template_assertion_predicate_count() const { return _template_assertion_predicate_opaqs.length(); }
int template_assertion_predicate_count() const { return _template_assertion_predicate_opaques.length(); }
int expensive_count() const { return _expensive_nodes.length(); }
int coarsened_count() const { return _coarsened_locks.length(); }

Node* macro_node(int idx) const { return _macro_nodes.at(idx); }
ParsePredicateNode* parse_predicate(int idx) const { return _parse_predicates.at(idx); }

Node* template_assertion_predicate_opaq_node(int idx) const {
return _template_assertion_predicate_opaqs.at(idx);
}

Node* expensive_node(int idx) const { return _expensive_nodes.at(idx); }

Expand Down Expand Up @@ -728,15 +733,15 @@ class Compile : public Phase {
}
}

void add_template_assertion_predicate_opaq(Node* n) {
assert(!_template_assertion_predicate_opaqs.contains(n),
void add_template_assertion_predicate_opaque(OpaqueTemplateAssertionPredicateNode* n) {
assert(!_template_assertion_predicate_opaques.contains(n),
"Duplicate entry in Template Assertion Predicate OpaqueTemplateAssertionPredicate list");
_template_assertion_predicate_opaqs.append(n);
_template_assertion_predicate_opaques.append(n);
}

void remove_template_assertion_predicate_opaq(Node* n) {
void remove_template_assertion_predicate_opaque(OpaqueTemplateAssertionPredicateNode* n) {
if (template_assertion_predicate_count() > 0) {
_template_assertion_predicate_opaqs.remove_if_existing(n);
_template_assertion_predicate_opaques.remove_if_existing(n);
}
}
void add_coarsened_locks(GrowableArray<AbstractLockNode*>& locks);
Expand Down
18 changes: 12 additions & 6 deletions src/hotspot/share/opto/ifnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#include "opto/connode.hpp"
#include "opto/loopnode.hpp"
#include "opto/phaseX.hpp"
#include "opto/predicates.hpp"
#include "opto/predicates_enums.hpp"
#include "opto/runtime.hpp"
#include "opto/rootnode.hpp"
#include "opto/subnode.hpp"
Expand Down Expand Up @@ -2169,7 +2169,7 @@ Node* RangeCheckNode::Ideal(PhaseGVN *phase, bool can_reshape) {
ParsePredicateNode::ParsePredicateNode(Node* control, Deoptimization::DeoptReason deopt_reason, PhaseGVN* gvn)
: IfNode(control, gvn->intcon(1), PROB_MAX, COUNT_UNKNOWN),
_deopt_reason(deopt_reason),
_useless(false) {
_predicate_state(PredicateState::Useful) {
init_class_id(Class_ParsePredicate);
gvn->C->add_parse_predicate(this);
gvn->C->record_for_post_loop_opts_igvn(this);
Expand All @@ -2186,6 +2186,11 @@ ParsePredicateNode::ParsePredicateNode(Node* control, Deoptimization::DeoptReaso
#endif // ASSERT
}

void ParsePredicateNode::mark_useless(PhaseIterGVN& igvn) {
_predicate_state = PredicateState::Useless;
igvn._worklist.push(this);
}

Node* ParsePredicateNode::uncommon_trap() const {
ParsePredicateUncommonProj* uncommon_proj = proj_out(0)->as_IfFalse();
Node* uct_region_or_call = uncommon_proj->unique_ctrl_out();
Expand All @@ -2195,14 +2200,15 @@ Node* ParsePredicateNode::uncommon_trap() const {

// Fold this node away once it becomes useless or at latest in post loop opts IGVN.
const Type* ParsePredicateNode::Value(PhaseGVN* phase) const {
assert(_predicate_state != PredicateState::MaybeUseful, "should only be MaybeUseful when eliminating useless "
"predicates during loop opts");
Comment on lines +2203 to +2204
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best effort assert to ensure we are not seeing MaybeUseful anywhere else except during Predicate elimination. Same for OpaqueTemplateAssertionPredicate.

if (phase->type(in(0)) == Type::TOP) {
return Type::TOP;
}
if (_useless || phase->C->post_loop_opts_phase()) {
if (_predicate_state == PredicateState::Useless || phase->C->post_loop_opts_phase()) {
return TypeTuple::IFTRUE;
} else {
return bottom_type();
}
return bottom_type();
}

#ifndef PRODUCT
Expand All @@ -2224,7 +2230,7 @@ void ParsePredicateNode::dump_spec(outputStream* st) const {
default:
fatal("unknown kind");
}
if (_useless) {
if (_predicate_state == PredicateState::Useless) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only print useless since MaybeUseful is only set for a very brief moment and should normally not be visible when dumping/in IGV dumps.

st->print("#useless ");
}
}
Expand Down
109 changes: 3 additions & 106 deletions src/hotspot/share/opto/loopnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4399,116 +4399,13 @@ void PhaseIdealLoop::log_loop_tree() {

// Eliminate all Parse and Template Assertion Predicates that are not associated with a loop anymore. The eliminated
// predicates will be removed during the next round of IGVN.
void PhaseIdealLoop::eliminate_useless_predicates() {
void PhaseIdealLoop::eliminate_useless_predicates() const {
if (C->parse_predicate_count() == 0 && C->template_assertion_predicate_count() == 0) {
return; // No predicates left.
}

eliminate_useless_parse_predicates();
eliminate_useless_template_assertion_predicates();
}

// Eliminate all Parse Predicates that do not belong to a loop anymore by marking them useless. These will be removed
// during the next round of IGVN.
void PhaseIdealLoop::eliminate_useless_parse_predicates() {
mark_all_parse_predicates_useless();
if (C->has_loops()) {
mark_loop_associated_parse_predicates_useful();
}
add_useless_parse_predicates_to_igvn_worklist();
}

void PhaseIdealLoop::mark_all_parse_predicates_useless() const {
for (int i = 0; i < C->parse_predicate_count(); i++) {
C->parse_predicate(i)->mark_useless();
}
}

void PhaseIdealLoop::mark_loop_associated_parse_predicates_useful() {
for (LoopTreeIterator iterator(_ltree_root); !iterator.done(); iterator.next()) {
IdealLoopTree* loop = iterator.current();
if (loop->can_apply_loop_predication()) {
mark_useful_parse_predicates_for_loop(loop);
}
}
}

// This visitor marks all visited Parse Predicates useful.
class ParsePredicateUsefulMarker : public PredicateVisitor {
public:
using PredicateVisitor::visit;

void visit(const ParsePredicate& parse_predicate) override {
parse_predicate.head()->mark_useful();
}
};

void PhaseIdealLoop::mark_useful_parse_predicates_for_loop(IdealLoopTree* loop) {
Node* entry = loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl);
const PredicateIterator predicate_iterator(entry);
ParsePredicateUsefulMarker useful_marker;
predicate_iterator.for_each(useful_marker);
}

void PhaseIdealLoop::add_useless_parse_predicates_to_igvn_worklist() {
for (int i = 0; i < C->parse_predicate_count(); i++) {
ParsePredicateNode* parse_predicate_node = C->parse_predicate(i);
if (parse_predicate_node->is_useless()) {
_igvn._worklist.push(parse_predicate_node);
}
}
}


// Eliminate all Template Assertion Predicates that do not belong to their originally associated loop anymore by
// replacing the OpaqueTemplateAssertionPredicate node of the If node with true. These nodes will be removed during the
// next round of IGVN.
void PhaseIdealLoop::eliminate_useless_template_assertion_predicates() {
Unique_Node_List useful_predicates;
if (C->has_loops()) {
collect_useful_template_assertion_predicates(useful_predicates);
}
eliminate_useless_template_assertion_predicates(useful_predicates);
}

void PhaseIdealLoop::collect_useful_template_assertion_predicates(Unique_Node_List& useful_predicates) {
for (LoopTreeIterator iterator(_ltree_root); !iterator.done(); iterator.next()) {
IdealLoopTree* loop = iterator.current();
if (loop->can_apply_loop_predication()) {
collect_useful_template_assertion_predicates_for_loop(loop, useful_predicates);
}
}
}

void PhaseIdealLoop::collect_useful_template_assertion_predicates_for_loop(IdealLoopTree* loop,
Unique_Node_List &useful_predicates) {
Node* entry = loop->_head->as_Loop()->skip_strip_mined()->in(LoopNode::EntryControl);
const Predicates predicates(entry);
if (UseProfiledLoopPredicate) {
const PredicateBlock* profiled_loop_predicate_block = predicates.profiled_loop_predicate_block();
if (profiled_loop_predicate_block->has_parse_predicate()) {
ParsePredicateSuccessProj* parse_predicate_proj = profiled_loop_predicate_block->parse_predicate_success_proj();
get_opaque_template_assertion_predicate_nodes(parse_predicate_proj, useful_predicates);
}
}

if (UseLoopPredicate) {
const PredicateBlock* loop_predicate_block = predicates.loop_predicate_block();
if (loop_predicate_block->has_parse_predicate()) {
ParsePredicateSuccessProj* parse_predicate_proj = loop_predicate_block->parse_predicate_success_proj();
get_opaque_template_assertion_predicate_nodes(parse_predicate_proj, useful_predicates);
}
}
}

void PhaseIdealLoop::eliminate_useless_template_assertion_predicates(Unique_Node_List& useful_predicates) const {
for (int i = C->template_assertion_predicate_count(); i > 0; i--) {
OpaqueTemplateAssertionPredicateNode* opaque_node =
C->template_assertion_predicate_opaq_node(i - 1)->as_OpaqueTemplateAssertionPredicate();
if (!useful_predicates.member(opaque_node)) { // not in the useful list
opaque_node->mark_useless(_igvn);
}
}
EliminateUselessPredicates eliminate_useless_predicates(_igvn, _ltree_root);
eliminate_useless_predicates.eliminate();
}

// If a post or main loop is removed due to an assert predicate, the opaque that guards the loop is not needed anymore
Expand Down
13 changes: 1 addition & 12 deletions src/hotspot/share/opto/loopnode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1442,18 +1442,7 @@ class PhaseIdealLoop : public PhaseTransform {
void eliminate_hoisted_range_check(IfTrueNode* hoisted_check_proj, IfTrueNode* template_assertion_predicate_proj);

// Helper function to collect predicate for eliminating the useless ones
void eliminate_useless_predicates();

void eliminate_useless_parse_predicates();
void mark_all_parse_predicates_useless() const;
void mark_loop_associated_parse_predicates_useful();
static void mark_useful_parse_predicates_for_loop(IdealLoopTree* loop);
void add_useless_parse_predicates_to_igvn_worklist();

void eliminate_useless_template_assertion_predicates();
void collect_useful_template_assertion_predicates(Unique_Node_List& useful_predicates);
static void collect_useful_template_assertion_predicates_for_loop(IdealLoopTree* loop, Unique_Node_List& useful_predicates);
void eliminate_useless_template_assertion_predicates(Unique_Node_List& useful_predicates) const;
void eliminate_useless_predicates() const;

void eliminate_useless_zero_trip_guard();
void eliminate_useless_multiversion_if();
Expand Down
5 changes: 4 additions & 1 deletion src/hotspot/share/opto/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,9 @@ Node *Node::clone() const {
if (n->is_ParsePredicate()) {
C->add_parse_predicate(n->as_ParsePredicate());
}
if (n->is_OpaqueTemplateAssertionPredicate()) {
C->add_template_assertion_predicate_opaque(n->as_OpaqueTemplateAssertionPredicate());
}
Comment on lines +514 to +516
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See PR description "Other Included Changes".


BarrierSetC2* bs = BarrierSet::barrier_set()->barrier_set_c2();
bs->register_potential_barrier_node(n);
Expand Down Expand Up @@ -607,7 +610,7 @@ void Node::destruct(PhaseValues* phase) {
compile->remove_expensive_node(this);
}
if (is_OpaqueTemplateAssertionPredicate()) {
compile->remove_template_assertion_predicate_opaq(this);
compile->remove_template_assertion_predicate_opaque(as_OpaqueTemplateAssertionPredicate());
}
if (is_ParsePredicate()) {
compile->remove_parse_predicate(as_ParsePredicate());
Expand Down
Loading