Skip to content

Commit

Permalink
8330386: Replace Opaque4Node of Initialized Assertion Predicate with …
Browse files Browse the repository at this point in the history
…new OpaqueInitializedAssertionPredicateNode
  • Loading branch information
chhagedorn committed Apr 25, 2024
1 parent b9927aa commit fc74c9e
Show file tree
Hide file tree
Showing 15 changed files with 532 additions and 64 deletions.
1 change: 1 addition & 0 deletions src/hotspot/share/opto/classes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,7 @@ macro(OpaqueLoopStride)
macro(OpaqueZeroTripGuard)
macro(Opaque3)
macro(Opaque4)
macro(OpaqueInitializedAssertionPredicate)
macro(ProfileBoolean)
macro(OrI)
macro(OrL)
Expand Down
7 changes: 5 additions & 2 deletions src/hotspot/share/opto/loopPredicate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,10 +349,13 @@ void PhaseIdealLoop::get_assertion_predicates(Node* predicate, Unique_Node_List&
if (uncommon_proj->unique_ctrl_out() != rgn) {
break;
}
if (iff->in(1)->Opcode() == Op_Opaque4 && assertion_predicate_has_loop_opaque_node(iff)) {
Node* bol = iff->in(1);
assert(!bol->is_OpaqueInitializedAssertionPredicate(), "should not find an Initialized Assertion Predicate");
if (bol->is_Opaque4()) {
assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes");
if (get_opaque) {
// Collect the predicate Opaque4 node.
list.push(iff->in(1));
list.push(bol);
} else {
// Collect the predicate projection.
list.push(predicate);
Expand Down
90 changes: 55 additions & 35 deletions src/hotspot/share/opto/loopTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1205,7 +1205,7 @@ bool IdealLoopTree::policy_range_check(PhaseIdealLoop* phase, bool provisional,
continue; // dead constant test
}
if (!bol->is_Bool()) {
assert(bol->Opcode() == Op_Conv2B, "predicate check only");
assert(bol->is_OpaqueInitializedAssertionPredicate(), "Initialized Assertion Predicate check only");
continue;
}
if (bol->as_Bool()->_test._test == BoolTest::ne) {
Expand Down Expand Up @@ -1385,8 +1385,10 @@ void PhaseIdealLoop::copy_assertion_predicates_to_main_loop_helper(const Predica
uncommon_proj = iff->proj_out(1 - predicate_proj->as_Proj()->_con);
if (uncommon_proj->unique_ctrl_out() != rgn)
break;
if (iff->in(1)->Opcode() == Op_Opaque4) {
assert(assertion_predicate_has_loop_opaque_node(iff), "unexpected");
Node* bol = iff->in(1);
assert(!bol->is_OpaqueInitializedAssertionPredicate(), "should not find an Initialized Assertion Predicate");
if (bol->is_Opaque4()) {
assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes");
// Clone the Assertion Predicate twice and initialize one with the initial
// value of the loop induction variable. Leave the other predicate
// to be initialized when increasing the stride during loop unrolling.
Expand Down Expand Up @@ -1496,20 +1498,25 @@ Node* PhaseIdealLoop::clone_assertion_predicate_and_initialize(Node* iff, Node*
Node* uncommon_proj, Node* control, IdealLoopTree* outer_loop,
Node* input_proj) {
TemplateAssertionPredicateExpression template_assertion_predicate_expression(iff->in(1)->as_Opaque4());
Opaque4Node* new_opaque4_node;
Node* new_opaque_node;
if (new_stride == nullptr) {
// Only set a new OpaqueLoopInitNode node and clone the existing OpaqueLoopStrideNode without modification.
// Clone the Template Assertion Predicate and set a new OpaqueLoopInitNode to create a new Template Assertion Predicate.
// This is done when creating a new Template Assertion Predicate for the main loop which requires a new init node.
// We keep the Opaque4 node since it's still a template.
assert(new_init->is_OpaqueLoopInit(), "only for creating new Template Assertion Predicates");
new_opaque4_node = template_assertion_predicate_expression.clone_and_replace_init(new_init, control, this);
new_opaque_node = template_assertion_predicate_expression.clone_and_replace_init(new_init, control, this);
} else {
new_opaque4_node = template_assertion_predicate_expression.clone_and_replace_init_and_stride(new_init, new_stride,
// Create an Initialized Assertion Predicate from the Template Assertion Predicate.
new_opaque_node = template_assertion_predicate_expression.clone_and_replace_init_and_stride(new_init, new_stride,
control, this);
// Since this is an Initialized Assertion Predicate, we use the dedicated opaque node.
new_opaque_node = new OpaqueInitializedAssertionPredicateNode(new_opaque_node->in(1)->as_Bool());
register_new_node(new_opaque_node, control);
}
Node* proj = predicate->clone();
Node* other_proj = uncommon_proj->clone();
Node* new_iff = iff->clone();
new_iff->set_req(1, new_opaque4_node);
new_iff->set_req(1, new_opaque_node);
proj->set_req(0, new_iff);
other_proj->set_req(0, new_iff);
Node* frame = new ParmNode(C->start(), TypeFunc::FramePtr);
Expand Down Expand Up @@ -1965,25 +1972,30 @@ void PhaseIdealLoop::update_main_loop_assertion_predicates(Node* ctrl, CountedLo
while (entry != nullptr && entry->is_Proj() && entry->in(0)->is_If()) {
IfNode* iff = entry->in(0)->as_If();
ProjNode* proj = iff->proj_out(1 - entry->as_Proj()->_con);
if (proj->unique_ctrl_out()->Opcode() != Op_Halt) {
if (!proj->unique_ctrl_out()->is_Halt()) {
break;
}
if (iff->in(1)->Opcode() == Op_Opaque4) {
if (!assertion_predicate_has_loop_opaque_node(iff)) {
// No OpaqueLoop* node? Then it's one of the two Initialized Assertion Predicates:
// - For the initial access a[init]
// - For the last access a[init+old_stride-orig_stride]
// We could keep the one for the initial access but we do not know which one we currently have here. Just kill both.
// We will create new Initialized Assertion Predicates from the Template Assertion Predicates below:
Node* bol = iff->in(1);
if (bol->is_Opaque4()) {
if (assertion_predicate_has_loop_opaque_node(iff)) {
// This is a Template Assertion Predicate for the initial or last access.
// Create an Initialized Assertion Predicates for it accordingly:
// - For the initial access a[init] (same as before)
// - For the last access a[init+new_stride-orig_stride] (with the new unroll stride)
_igvn.replace_input_of(iff, 1, iff->in(1)->in(2));
} else {
// Template Assertion Predicate: Clone it to create initialized version with new stride.
prev_proj = clone_assertion_predicate_and_initialize(iff, init, max_value, entry, proj, ctrl, outer_loop,
prev_proj);
assert(!assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), "unexpected");
} else {
// Ignore Opaque4 from a null-check for an intrinsic or unsafe access. This could happen when we maximally
// unroll a non-main loop with such an If with an Opaque4 node directly above the loop entry.
assert(!loop_head->is_main_loop(), "Opaque4 node from a non-null check - should not be at main loop");
}
} else if (bol->is_OpaqueInitializedAssertionPredicate()) {
// This is one of the two Initialized Assertion Predicates:
// - For the initial access a[init]
// - For the last access a[init+old_stride-orig_stride]
// We could keep the one for the initial access but we do not know which one we currently have here. Just kill both.
_igvn.replace_input_of(iff, 1, _igvn.intcon(1));
}
entry = entry->in(0)->in(0);
}
Expand All @@ -2006,13 +2018,14 @@ void PhaseIdealLoop::copy_assertion_predicates_to_post_loop(LoopNode* main_loop_
while (ctrl != nullptr && ctrl->is_Proj() && ctrl->in(0)->is_If()) {
IfNode* iff = ctrl->in(0)->as_If();
ProjNode* proj = iff->proj_out(1 - ctrl->as_Proj()->_con);
if (proj->unique_ctrl_out()->Opcode() != Op_Halt) {
if (!proj->unique_ctrl_out()->is_Halt()) {
break;
}
if (iff->in(1)->Opcode() == Op_Opaque4 && assertion_predicate_has_loop_opaque_node(iff)) {
if (iff->in(1)->is_Opaque4()) {
assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes");
prev_proj = clone_assertion_predicate_and_initialize(iff, init, stride, ctrl, proj, post_loop_entry,
post_loop, prev_proj);
assert(!assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), "unexpected");
assert(!assertion_predicate_has_loop_opaque_node(prev_proj->in(0)->as_If()), "must not find OpaqueLoop* nodes");
}
ctrl = ctrl->in(0)->in(0);
}
Expand Down Expand Up @@ -2043,8 +2056,10 @@ void PhaseIdealLoop::initialize_assertion_predicates_for_peeled_loop(const Predi
// Does not belong to this Predicate Block anymore.
break;
}
if (iff->in(1)->Opcode() == Op_Opaque4) {
assert(assertion_predicate_has_loop_opaque_node(iff), "unexpected");
Node* bol = iff->in(1);
assert(!bol->is_OpaqueInitializedAssertionPredicate(), "should not find an Initialized Assertion Predicate");
if (bol->is_Opaque4()) {
assert(assertion_predicate_has_loop_opaque_node(iff), "must find OpaqueLoop* nodes");
input_proj = clone_assertion_predicate_and_initialize(iff, init, stride, next_regular_predicate_proj, uncommon_proj, control,
outer_loop, input_proj);

Expand Down Expand Up @@ -2767,20 +2782,25 @@ 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) {
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) {
bool overflow = false;
BoolNode* bol = rc_predicate(loop, ctrl, scale_con, offset, value, nullptr, stride_con,
limit, (stride_con > 0) != (scale_con > 0), overflow);
Node* opaque_bol = new Opaque4Node(C, bol, _igvn.intcon(1));
register_new_node(opaque_bol, ctrl);
Node* opaque_assertion_predicate;
if (is_template) {
opaque_assertion_predicate = new Opaque4Node(C, bol, _igvn.intcon(1));
} else {
opaque_assertion_predicate = new OpaqueInitializedAssertionPredicateNode(bol);
}
register_new_node(opaque_assertion_predicate, ctrl);
IfNode* new_iff = nullptr;
if (overflow) {
new_iff = new IfNode(ctrl, opaque_bol, PROB_MAX, COUNT_UNKNOWN);
new_iff = new IfNode(ctrl, opaque_assertion_predicate, PROB_MAX, COUNT_UNKNOWN);
} else {
new_iff = new RangeCheckNode(ctrl, opaque_bol, PROB_MAX, COUNT_UNKNOWN);
new_iff = new RangeCheckNode(ctrl, opaque_assertion_predicate, PROB_MAX, COUNT_UNKNOWN);
}
register_control(new_iff, loop->_parent, ctrl);
Node* iffalse = new IfFalseNode(new_iff);
Expand Down Expand Up @@ -2981,13 +3001,13 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {

// Initialized Assertion Predicate for the value of the initial main-loop.
loop_entry = add_range_check_elimination_assertion_predicate(loop, loop_entry, scale_con, int_offset,
int_limit, stride_con, init);
int_limit, stride_con, init, false);
assert(!assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");

// 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);
int_limit, stride_con, opaque_init, true);
assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");

Node* opaque_stride = new OpaqueLoopStrideNode(C, cl->stride());
Expand All @@ -3000,7 +3020,7 @@ void PhaseIdealLoop::do_range_check(IdealLoopTree *loop, Node_List &old_new) {
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);
int_limit, stride_con, max_value, true);
assert(assertion_predicate_has_loop_opaque_node(loop_entry->in(0)->as_If()), "unexpected");

} else {
Expand Down
7 changes: 3 additions & 4 deletions src/hotspot/share/opto/loopnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4371,10 +4371,9 @@ void PhaseIdealLoop::collect_useful_template_assertion_predicates_for_loop(Ideal

void PhaseIdealLoop::eliminate_useless_template_assertion_predicates(Unique_Node_List& useful_predicates) {
for (int i = C->template_assertion_predicate_count(); i > 0; i--) {
Node* opaque4 = C->template_assertion_predicate_opaq_node(i - 1);
assert(opaque4->Opcode() == Op_Opaque4, "must be");
if (!useful_predicates.member(opaque4)) { // not in the useful list
_igvn.replace_node(opaque4, opaque4->in(2));
Opaque4Node* opaque4_node = C->template_assertion_predicate_opaq_node(i - 1)->as_Opaque4();
if (!useful_predicates.member(opaque4_node)) { // not in the useful list
_igvn.replace_node(opaque4_node, opaque4_node->in(2));
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/hotspot/share/opto/loopnode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1380,7 +1380,8 @@ class PhaseIdealLoop : public PhaseTransform {
IfProjNode* upper_bound_proj, int scale, Node* offset, Node* init, Node* limit,
jint stride, Node* rng, bool& overflow, Deoptimization::DeoptReason reason);
Node* add_range_check_elimination_assertion_predicate(IdealLoopTree* loop, Node* predicate_proj, int scale_con,
Node* offset, Node* limit, jint stride_con, Node* value);
Node* offset, Node* limit, int stride_con, Node* value,
bool is_template);

// Helper function to collect predicate for eliminating the useless ones
void eliminate_useless_predicates();
Expand Down
19 changes: 11 additions & 8 deletions src/hotspot/share/opto/loopopts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -778,8 +778,10 @@ Node *PhaseIdealLoop::conditional_move( Node *region ) {
}
}//for
Node* bol = iff->in(1);
if (bol->Opcode() == Op_Opaque4) {
return nullptr; // Ignore loop predicate checks (the Opaque4 ensures they will go away)
assert(!bol->is_OpaqueInitializedAssertionPredicate(), "Initialized Assertion Predicates cannot form a diamond");
if (bol->is_Opaque4()) {
// Ignore Template Assertion Predicates with Opaque4 nodes.
return nullptr;
}
assert(bol->Opcode() == Op_Bool, "Unexpected node");
int cmp_op = bol->in(1)->Opcode();
Expand Down Expand Up @@ -1662,7 +1664,8 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
!n->is_Proj() &&
!n->is_MergeMem() &&
!n->is_CMove() &&
n->Opcode() != Op_Opaque4 &&
!n->is_Opaque4() &&
!n->is_OpaqueInitializedAssertionPredicate() &&
!n->is_Type()) {
Node *n_ctrl = get_ctrl(n);
IdealLoopTree *n_loop = get_loop(n_ctrl);
Expand Down Expand Up @@ -1975,18 +1978,18 @@ Node* PhaseIdealLoop::clone_iff(PhiNode* phi) {
// Convert this Phi into a Phi merging Bools
uint i;
for (i = 1; i < phi->req(); i++) {
Node *b = phi->in(i);
Node* b = phi->in(i);
if (b->is_Phi()) {
_igvn.replace_input_of(phi, i, clone_iff(b->as_Phi()));
} else {
assert(b->is_Bool() || b->Opcode() == Op_Opaque4, "");
assert(b->is_Bool() || b->is_Opaque4() || b->is_OpaqueInitializedAssertionPredicate(),
"bool, non-null check with Opaque4 node or Initialized Assertion Predicate with its Opaque node");
}
}

Node* n = phi->in(1);
Node* sample_opaque = nullptr;
Node *sample_bool = nullptr;
if (n->Opcode() == Op_Opaque4) {
if (n->is_Opaque4() || n->is_OpaqueInitializedAssertionPredicate()) {
sample_opaque = n;
sample_bool = n->in(1);
assert(sample_bool->is_Bool(), "wrong type");
Expand Down Expand Up @@ -2159,7 +2162,7 @@ void PhaseIdealLoop::clone_loop_handle_data_uses(Node* old, Node_List &old_new,
// make sure the Bool/Cmp input is cloned down to avoid a Phi between
// the AllocateArray node and its ValidLengthTest input that could cause
// split if to break.
if (use->is_If() || use->is_CMove() || use->Opcode() == Op_Opaque4 ||
if (use->is_If() || use->is_CMove() || use->is_Opaque4() ||
(use->Opcode() == Op_AllocateArray && use->in(AllocateNode::ValidLengthTest) == old)) {
// Since this code is highly unlikely, we lazily build the worklist
// of such Nodes to go split.
Expand Down
4 changes: 2 additions & 2 deletions src/hotspot/share/opto/macro.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2427,7 +2427,7 @@ void PhaseMacroExpand::eliminate_macro_nodes() {
default:
assert(n->Opcode() == Op_LoopLimit ||
n->Opcode() == Op_Opaque3 ||
n->Opcode() == Op_Opaque4 ||
n->is_Opaque4() ||
n->Opcode() == Op_MaxL ||
n->Opcode() == Op_MinL ||
BarrierSet::barrier_set()->barrier_set_c2()->is_gc_barrier_node(n),
Expand Down Expand Up @@ -2502,7 +2502,7 @@ bool PhaseMacroExpand::expand_macro_nodes() {
_igvn.replace_node(n, repl);
success = true;
#endif
} else if (n->Opcode() == Op_Opaque4) {
} else if (n->is_Opaque4()) {
// With Opaque4 nodes, the expectation is that the test of input 1
// is always equal to the constant value of input 2. So we can
// remove the Opaque4 and replace it by input 2. In debug builds,
Expand Down
2 changes: 1 addition & 1 deletion src/hotspot/share/opto/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,7 +610,7 @@ void Node::destruct(PhaseValues* phase) {
if (is_expensive()) {
compile->remove_expensive_node(this);
}
if (Opcode() == Op_Opaque4) {
if (is_Opaque4()) {
compile->remove_template_assertion_predicate_opaq(this);
}
if (is_ParsePredicate()) {
Expand Down
9 changes: 6 additions & 3 deletions src/hotspot/share/opto/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ class Opaque1Node;
class OpaqueLoopInitNode;
class OpaqueLoopStrideNode;
class Opaque4Node;
class OpaqueInitializedAssertionPredicateNode;
class OuterStripMinedLoopNode;
class OuterStripMinedLoopEndNode;
class Node;
Expand Down Expand Up @@ -795,9 +796,10 @@ class Node {
DEFINE_CLASS_ID(OpaqueLoopInit, Opaque1, 0)
DEFINE_CLASS_ID(OpaqueLoopStride, Opaque1, 1)
DEFINE_CLASS_ID(Opaque4, Node, 17)
DEFINE_CLASS_ID(Move, Node, 18)
DEFINE_CLASS_ID(LShift, Node, 19)
DEFINE_CLASS_ID(Neg, Node, 20)
DEFINE_CLASS_ID(OpaqueInitializedAssertionPredicate, Node, 18)
DEFINE_CLASS_ID(Move, Node, 19)
DEFINE_CLASS_ID(LShift, Node, 20)
DEFINE_CLASS_ID(Neg, Node, 21)

_max_classes = ClassMask_Neg
};
Expand Down Expand Up @@ -966,6 +968,7 @@ class Node {
DEFINE_CLASS_QUERY(NeverBranch)
DEFINE_CLASS_QUERY(Opaque1)
DEFINE_CLASS_QUERY(Opaque4)
DEFINE_CLASS_QUERY(OpaqueInitializedAssertionPredicate)
DEFINE_CLASS_QUERY(OpaqueLoopInit)
DEFINE_CLASS_QUERY(OpaqueLoopStride)
DEFINE_CLASS_QUERY(OuterStripMinedLoop)
Expand Down
Loading

0 comments on commit fc74c9e

Please sign in to comment.