8253644: C2: assert(skeleton_predicate_has_opaque(iff)) failed: unexpected
Reviewed-by: roland, kvn
This commit is contained in:
parent
51ac37686c
commit
1d0adbb812
src/hotspot/share/opto
test/hotspot/jtreg/compiler/loopopts
@ -227,19 +227,17 @@ ProjNode* PhaseIdealLoop::clone_predicate_to_unswitched_loop(ProjNode* predicate
|
||||
return new_predicate_proj;
|
||||
}
|
||||
|
||||
// Clones skeleton predicates starting at 'old_predicate_proj' to
|
||||
// 'new_predicate_proj' and rewires the control edges of data nodes in
|
||||
// the loop from the old predicates to the new cloned predicates.
|
||||
// Clones skeleton predicates starting at 'old_predicate_proj' by following its control inputs and rewires the control edges of in the loop from
|
||||
// the old predicates to the new cloned predicates.
|
||||
void PhaseIdealLoop::clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, Deoptimization::DeoptReason reason,
|
||||
ProjNode* old_predicate_proj, ProjNode* iffast, ProjNode* ifslow) {
|
||||
ProjNode* old_predicate_proj, ProjNode* iffast_pred, ProjNode* ifslow_pred) {
|
||||
IfNode* iff = old_predicate_proj->in(0)->as_If();
|
||||
assert(iffast_pred->in(0)->is_If() && ifslow_pred->in(0)->is_If(), "sanity check");
|
||||
ProjNode* uncommon_proj = iff->proj_out(1 - old_predicate_proj->as_Proj()->_con);
|
||||
Node* rgn = uncommon_proj->unique_ctrl_out();
|
||||
assert(rgn->is_Region() || rgn->is_Call(), "must be a region or call uct");
|
||||
assert(iff->in(1)->in(1)->Opcode() == Op_Opaque1, "unexpected predicate shape");
|
||||
Node* predicate = iff->in(0);
|
||||
Node* current_proj = old_predicate_proj;
|
||||
Node* prev_proj = current_proj;
|
||||
Unique_Node_List list;
|
||||
while (predicate != NULL && predicate->is_Proj() && predicate->in(0)->is_If()) {
|
||||
iff = predicate->in(0)->as_If();
|
||||
@ -256,35 +254,33 @@ void PhaseIdealLoop::clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree*
|
||||
}
|
||||
|
||||
Node_List to_process;
|
||||
// Process in reverse order such that 'create_new_if_for_predicate' can be used and the original order is maintained
|
||||
for (int i = list.size()-1; i >= 0; i--) {
|
||||
// Process in reverse order such that 'create_new_if_for_predicate' can be used in 'clone_skeleton_predicate_for_unswitched_loops'
|
||||
// and the original order is maintained.
|
||||
for (int i = list.size() - 1; i >= 0; i--) {
|
||||
predicate = list.at(i);
|
||||
assert(predicate->in(0)->is_If(), "must be If node");
|
||||
iff = predicate->in(0)->as_If();
|
||||
assert(predicate->is_Proj() && predicate->as_Proj()->is_IfProj(), "predicate must be a projection of an if node");
|
||||
IfProjNode* predicate_proj = predicate->as_IfProj();
|
||||
|
||||
// cloned_proj is the same type of projection as the original predicate projection (IfTrue or IfFalse)
|
||||
ProjNode* fast_proj = create_new_if_for_predicate(iffast, NULL, reason, iff->Opcode(), predicate_proj->is_IfTrue());
|
||||
ProjNode* slow_proj = create_new_if_for_predicate(ifslow, NULL, reason, iff->Opcode(), predicate_proj->is_IfTrue());
|
||||
ProjNode* fast_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, iffast_pred, loop);
|
||||
assert(skeleton_predicate_has_opaque(fast_proj->in(0)->as_If()), "must find skeleton predicate for fast loop");
|
||||
ProjNode* slow_proj = clone_skeleton_predicate_for_unswitched_loops(iff, predicate_proj, uncommon_proj, reason, ifslow_pred, loop);
|
||||
assert(skeleton_predicate_has_opaque(slow_proj->in(0)->as_If()), "must find skeleton predicate for slow loop");
|
||||
|
||||
// Replace bool input by input from original predicate
|
||||
_igvn.replace_input_of(fast_proj->in(0), 1, iff->in(1));
|
||||
_igvn.replace_input_of(slow_proj->in(0), 1, iff->in(1));
|
||||
|
||||
for (DUIterator i = predicate->outs(); predicate->has_out(i); i++) {
|
||||
Node* fast_node = predicate->out(i);
|
||||
// Update control dependent data nodes.
|
||||
for (DUIterator j = predicate->outs(); predicate->has_out(j); j++) {
|
||||
Node* fast_node = predicate->out(j);
|
||||
if (loop->is_member(get_loop(ctrl_or_self(fast_node)))) {
|
||||
assert(fast_node->in(0) == predicate, "only control edge");
|
||||
Node* slow_node = old_new[fast_node->_idx];
|
||||
assert(slow_node->in(0) == predicate, "only control edge");
|
||||
_igvn.replace_input_of(fast_node, 0, fast_proj);
|
||||
to_process.push(slow_node);
|
||||
--i;
|
||||
--j;
|
||||
}
|
||||
}
|
||||
// Have to delay updates to the slow loop so uses of predicate are
|
||||
// not modified while we iterate on them.
|
||||
// Have to delay updates to the slow loop so uses of predicate are not modified while we iterate on them.
|
||||
while (to_process.size() > 0) {
|
||||
Node* slow_node = to_process.pop();
|
||||
_igvn.replace_input_of(slow_node, 0, slow_proj);
|
||||
@ -292,9 +288,23 @@ void PhaseIdealLoop::clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree*
|
||||
}
|
||||
}
|
||||
|
||||
// Clone a skeleton predicate for an unswitched loop. OpaqueLoopInit and OpaqueLoopStride nodes are cloned and uncommon
|
||||
// traps are kept for the predicate (a Halt node is used later when creating pre/main/post loops and copying this cloned
|
||||
// predicate again).
|
||||
ProjNode* PhaseIdealLoop::clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj,
|
||||
Deoptimization::DeoptReason reason, ProjNode* output_proj,
|
||||
IdealLoopTree* loop) {
|
||||
Node* bol = clone_skeleton_predicate_bool(iff, NULL, NULL, predicate, uncommon_proj, output_proj, loop);
|
||||
ProjNode* proj = create_new_if_for_predicate(output_proj, NULL, reason, iff->Opcode(), predicate->is_IfTrue());
|
||||
_igvn.replace_input_of(proj->in(0), 1, bol);
|
||||
_igvn.replace_input_of(output_proj->in(0), 0, proj);
|
||||
set_idom(output_proj->in(0), proj, dom_depth(proj));
|
||||
return proj;
|
||||
}
|
||||
|
||||
//--------------------------clone_loop_predicates-----------------------
|
||||
// Clone loop predicates to cloned loops when unswitching a loop.
|
||||
void PhaseIdealLoop::clone_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, ProjNode*& iffast, ProjNode*& ifslow) {
|
||||
void PhaseIdealLoop::clone_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, ProjNode*& iffast_pred, ProjNode*& ifslow_pred) {
|
||||
LoopNode* head = loop->_head->as_Loop();
|
||||
bool clone_limit_check = !head->is_CountedLoop();
|
||||
Node* entry = head->skip_strip_mined()->in(LoopNode::EntryControl);
|
||||
@ -318,31 +328,31 @@ void PhaseIdealLoop::clone_predicates_to_unswitched_loop(IdealLoopTree* loop, co
|
||||
}
|
||||
if (predicate_proj != NULL) { // right pattern that can be used by loop predication
|
||||
// clone predicate
|
||||
iffast = clone_predicate_to_unswitched_loop(predicate_proj, iffast, Deoptimization::Reason_predicate);
|
||||
ifslow = clone_predicate_to_unswitched_loop(predicate_proj, ifslow, Deoptimization::Reason_predicate);
|
||||
clone_skeleton_predicates_to_unswitched_loop(loop, old_new, Deoptimization::Reason_predicate, predicate_proj, iffast, ifslow);
|
||||
iffast_pred = clone_predicate_to_unswitched_loop(predicate_proj, iffast_pred, Deoptimization::Reason_predicate);
|
||||
ifslow_pred = clone_predicate_to_unswitched_loop(predicate_proj, ifslow_pred, Deoptimization::Reason_predicate);
|
||||
clone_skeleton_predicates_to_unswitched_loop(loop, old_new, Deoptimization::Reason_predicate, predicate_proj, iffast_pred, ifslow_pred);
|
||||
|
||||
check_created_predicate_for_unswitching(iffast);
|
||||
check_created_predicate_for_unswitching(ifslow);
|
||||
check_created_predicate_for_unswitching(iffast_pred);
|
||||
check_created_predicate_for_unswitching(ifslow_pred);
|
||||
}
|
||||
if (profile_predicate_proj != NULL) { // right pattern that can be used by loop predication
|
||||
// clone predicate
|
||||
iffast = clone_predicate_to_unswitched_loop(profile_predicate_proj, iffast, Deoptimization::Reason_profile_predicate);
|
||||
ifslow = clone_predicate_to_unswitched_loop(profile_predicate_proj, ifslow, Deoptimization::Reason_profile_predicate);
|
||||
clone_skeleton_predicates_to_unswitched_loop(loop, old_new, Deoptimization::Reason_profile_predicate, profile_predicate_proj, iffast, ifslow);
|
||||
iffast_pred = clone_predicate_to_unswitched_loop(profile_predicate_proj, iffast_pred, Deoptimization::Reason_profile_predicate);
|
||||
ifslow_pred = clone_predicate_to_unswitched_loop(profile_predicate_proj, ifslow_pred, Deoptimization::Reason_profile_predicate);
|
||||
clone_skeleton_predicates_to_unswitched_loop(loop, old_new, Deoptimization::Reason_profile_predicate, profile_predicate_proj, iffast_pred, ifslow_pred);
|
||||
|
||||
check_created_predicate_for_unswitching(iffast);
|
||||
check_created_predicate_for_unswitching(ifslow);
|
||||
check_created_predicate_for_unswitching(iffast_pred);
|
||||
check_created_predicate_for_unswitching(ifslow_pred);
|
||||
}
|
||||
if (limit_check_proj != NULL && clone_limit_check) {
|
||||
// Clone loop limit check last to insert it before loop.
|
||||
// Don't clone a limit check which was already finalized
|
||||
// for this counted loop (only one limit check is needed).
|
||||
iffast = clone_predicate_to_unswitched_loop(limit_check_proj, iffast, Deoptimization::Reason_loop_limit_check);
|
||||
ifslow = clone_predicate_to_unswitched_loop(limit_check_proj, ifslow, Deoptimization::Reason_loop_limit_check);
|
||||
iffast_pred = clone_predicate_to_unswitched_loop(limit_check_proj, iffast_pred, Deoptimization::Reason_loop_limit_check);
|
||||
ifslow_pred = clone_predicate_to_unswitched_loop(limit_check_proj, ifslow_pred, Deoptimization::Reason_loop_limit_check);
|
||||
|
||||
check_created_predicate_for_unswitching(iffast);
|
||||
check_created_predicate_for_unswitching(ifslow);
|
||||
check_created_predicate_for_unswitching(iffast_pred);
|
||||
check_created_predicate_for_unswitching(ifslow_pred);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1256,10 +1256,10 @@ void PhaseIdealLoop::copy_skeleton_predicates_to_main_loop_helper(Node* predicat
|
||||
// Clone the skeleton 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.
|
||||
prev_proj = clone_skeleton_predicate(iff, opaque_init, NULL, predicate, uncommon_proj, current_proj, outer_loop, prev_proj);
|
||||
prev_proj = clone_skeleton_predicate_for_main_loop(iff, opaque_init, NULL, predicate, uncommon_proj, current_proj, outer_loop, prev_proj);
|
||||
assert(skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "");
|
||||
|
||||
prev_proj = clone_skeleton_predicate(iff, init, stride, predicate, uncommon_proj, current_proj, outer_loop, prev_proj);
|
||||
prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, stride, predicate, uncommon_proj, current_proj, outer_loop, prev_proj);
|
||||
assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "");
|
||||
|
||||
// Rewire any control inputs from the cloned skeleton predicates down to the main and post loop for data nodes that are part of the
|
||||
@ -1333,12 +1333,17 @@ bool PhaseIdealLoop::skeleton_predicate_has_opaque(IfNode* iff) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Node* PhaseIdealLoop::clone_skeleton_predicate(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj,
|
||||
Node* current_proj, IdealLoopTree* outer_loop, Node* prev_proj) {
|
||||
// Clone the skeleton predicate bool for a main or unswitched loop:
|
||||
// Main loop: Set new_init and new_stride nodes as new inputs.
|
||||
// Unswitched loop: new_init and new_stride are both NULL. Clone OpaqueLoopInit and OpaqueLoopStride instead.
|
||||
Node* PhaseIdealLoop::clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj,
|
||||
Node* control, IdealLoopTree* outer_loop) {
|
||||
Node_Stack to_clone(2);
|
||||
to_clone.push(iff->in(1), 1);
|
||||
uint current = C->unique();
|
||||
Node* result = NULL;
|
||||
bool is_unswitched_loop = new_init == NULL && new_stride == NULL;
|
||||
assert(new_init != NULL || is_unswitched_loop, "new_init must be set when new_stride is non-null");
|
||||
// Look for the opaque node to replace with the new value
|
||||
// and clone everything in between. We keep the Opaque4 node
|
||||
// so the duplicated predicates are eliminated once loop
|
||||
@ -1350,25 +1355,33 @@ Node* PhaseIdealLoop::clone_skeleton_predicate(Node* iff, Node* new_init, Node*
|
||||
Node* m = n->in(i);
|
||||
int op = m->Opcode();
|
||||
if (skeleton_follow_inputs(m, op)) {
|
||||
to_clone.push(m, 1);
|
||||
continue;
|
||||
to_clone.push(m, 1);
|
||||
continue;
|
||||
}
|
||||
if (m->is_Opaque1()) {
|
||||
if (n->_idx < current) {
|
||||
n = n->clone();
|
||||
register_new_node(n, current_proj);
|
||||
register_new_node(n, control);
|
||||
}
|
||||
if (op == Op_OpaqueLoopInit) {
|
||||
if (is_unswitched_loop && m->_idx < current && new_init == NULL) {
|
||||
new_init = m->clone();
|
||||
register_new_node(new_init, control);
|
||||
}
|
||||
n->set_req(i, new_init);
|
||||
} else {
|
||||
assert(op == Op_OpaqueLoopStride, "unexpected opaque node");
|
||||
if (is_unswitched_loop && m->_idx < current && new_stride == NULL) {
|
||||
new_stride = m->clone();
|
||||
register_new_node(new_stride, control);
|
||||
}
|
||||
if (new_stride != NULL) {
|
||||
n->set_req(i, new_stride);
|
||||
}
|
||||
}
|
||||
to_clone.set_node(n);
|
||||
}
|
||||
for (;;) {
|
||||
while (true) {
|
||||
Node* cur = to_clone.node();
|
||||
uint j = to_clone.index();
|
||||
if (j+1 < cur->req()) {
|
||||
@ -1386,7 +1399,7 @@ Node* PhaseIdealLoop::clone_skeleton_predicate(Node* iff, Node* new_init, Node*
|
||||
assert(cur->_idx >= current || next->in(j)->Opcode() == Op_Opaque1, "new node or Opaque1 being replaced");
|
||||
if (next->_idx < current) {
|
||||
next = next->clone();
|
||||
register_new_node(next, current_proj);
|
||||
register_new_node(next, control);
|
||||
to_clone.set_node(next);
|
||||
}
|
||||
next->set_req(j, cur);
|
||||
@ -1394,21 +1407,29 @@ Node* PhaseIdealLoop::clone_skeleton_predicate(Node* iff, Node* new_init, Node*
|
||||
}
|
||||
} while (result == NULL);
|
||||
assert(result->_idx >= current, "new node expected");
|
||||
assert(!is_unswitched_loop || new_init != NULL, "new_init must always be found and cloned");
|
||||
return result;
|
||||
}
|
||||
|
||||
// Clone a skeleton predicate for the main loop. new_init and new_stride are set as new inputs. Since the predicates cannot fail at runtime,
|
||||
// Halt nodes are inserted instead of uncommon traps.
|
||||
Node* PhaseIdealLoop::clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj,
|
||||
Node* control, IdealLoopTree* outer_loop, Node* input_proj) {
|
||||
Node* result = clone_skeleton_predicate_bool(iff, new_init, new_stride, predicate, uncommon_proj, control, outer_loop);
|
||||
Node* proj = predicate->clone();
|
||||
Node* other_proj = uncommon_proj->clone();
|
||||
Node* new_iff = iff->clone();
|
||||
new_iff->set_req(1, result);
|
||||
proj->set_req(0, new_iff);
|
||||
other_proj->set_req(0, new_iff);
|
||||
Node *frame = new ParmNode(C->start(), TypeFunc::FramePtr);
|
||||
Node* frame = new ParmNode(C->start(), TypeFunc::FramePtr);
|
||||
register_new_node(frame, C->start());
|
||||
// It's impossible for the predicate to fail at runtime. Use an Halt node.
|
||||
Node* halt = new HaltNode(other_proj, frame, "duplicated predicate failed which is impossible");
|
||||
C->root()->add_req(halt);
|
||||
new_iff->set_req(0, prev_proj);
|
||||
new_iff->set_req(0, input_proj);
|
||||
|
||||
register_control(new_iff, outer_loop->_parent, prev_proj);
|
||||
register_control(new_iff, outer_loop->_parent, input_proj);
|
||||
register_control(proj, outer_loop->_parent, new_iff);
|
||||
register_control(other_proj, _ltree_root, new_iff);
|
||||
register_control(halt, _ltree_root, other_proj);
|
||||
@ -1904,7 +1925,7 @@ void PhaseIdealLoop::update_main_loop_skeleton_predicates(Node* ctrl, CountedLoo
|
||||
_igvn.replace_input_of(iff, 1, iff->in(1)->in(2));
|
||||
} else {
|
||||
// Add back predicates updated for the new stride.
|
||||
prev_proj = clone_skeleton_predicate(iff, init, max_value, entry, proj, ctrl, outer_loop, prev_proj);
|
||||
prev_proj = clone_skeleton_predicate_for_main_loop(iff, init, max_value, entry, proj, ctrl, outer_loop, prev_proj);
|
||||
assert(!skeleton_predicate_has_opaque(prev_proj->in(0)->as_If()), "unexpected");
|
||||
}
|
||||
}
|
||||
|
@ -910,8 +910,10 @@ private:
|
||||
void copy_skeleton_predicates_to_main_loop(CountedLoopNode* pre_head, Node* init, Node* stride, IdealLoopTree* outer_loop, LoopNode* outer_main_head,
|
||||
uint dd_main_head, const uint idx_before_pre_post, const uint idx_after_post_before_pre,
|
||||
Node* zero_trip_guard_proj_main, Node* zero_trip_guard_proj_post, const Node_List &old_new);
|
||||
Node* clone_skeleton_predicate(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj,
|
||||
Node* current_proj, IdealLoopTree* outer_loop, Node* prev_proj);
|
||||
Node* clone_skeleton_predicate_for_main_loop(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control,
|
||||
IdealLoopTree* outer_loop, Node* input_proj);
|
||||
Node* clone_skeleton_predicate_bool(Node* iff, Node* new_init, Node* new_stride, Node* predicate, Node* uncommon_proj, Node* control,
|
||||
IdealLoopTree* outer_loop);
|
||||
bool skeleton_predicate_has_opaque(IfNode* iff);
|
||||
void update_main_loop_skeleton_predicates(Node* ctrl, CountedLoopNode* loop_head, Node* init, int stride_con);
|
||||
void insert_loop_limit_check(ProjNode* limit_check_proj, Node* cmp_limit, Node* bol);
|
||||
@ -1557,10 +1559,12 @@ private:
|
||||
}
|
||||
|
||||
// Clone loop predicates to slow and fast loop when unswitching a loop
|
||||
void clone_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, ProjNode*& iffast, ProjNode*& ifslow);
|
||||
void clone_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, ProjNode*& iffast_pred, ProjNode*& ifslow_pred);
|
||||
ProjNode* clone_predicate_to_unswitched_loop(ProjNode* predicate_proj, Node* new_entry, Deoptimization::DeoptReason reason);
|
||||
void clone_skeleton_predicates_to_unswitched_loop(IdealLoopTree* loop, const Node_List& old_new, Deoptimization::DeoptReason reason,
|
||||
ProjNode* old_predicate_proj, ProjNode* iffast, ProjNode* ifslow);
|
||||
ProjNode* old_predicate_proj, ProjNode* iffast_pred, ProjNode* ifslow_pred);
|
||||
ProjNode* clone_skeleton_predicate_for_unswitched_loops(Node* iff, ProjNode* predicate, Node* uncommon_proj, Deoptimization::DeoptReason reason,
|
||||
ProjNode* output_proj, IdealLoopTree* loop);
|
||||
void check_created_predicate_for_unswitching(const Node* new_entry) const PRODUCT_RETURN;
|
||||
|
||||
bool _created_loop_node;
|
||||
|
@ -0,0 +1,225 @@
|
||||
/*
|
||||
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8253644
|
||||
* @summary Test the complete cloning of skeleton predicates to unswitched loops as done when cloning them to the main loop.
|
||||
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,compiler.loopopts.TestUnswitchCloneSkeletonPredicates::*
|
||||
* compiler.loopopts.TestUnswitchCloneSkeletonPredicates
|
||||
* @run main/othervm -Xcomp -XX:-PartialPeelLoop -XX:CompileCommand=compileonly,compiler.loopopts.TestUnswitchCloneSkeletonPredicates::*
|
||||
* compiler.loopopts.TestUnswitchCloneSkeletonPredicates
|
||||
* @run main/othervm -XX:-PartialPeelLoop compiler.loopopts.TestUnswitchCloneSkeletonPredicates
|
||||
*/
|
||||
package compiler.loopopts;
|
||||
|
||||
public class TestUnswitchCloneSkeletonPredicates {
|
||||
|
||||
static int x = 0;
|
||||
static int y = 20;
|
||||
static int intArr[] = new int[21000];
|
||||
static int idx = 0;
|
||||
static boolean bFld = true;
|
||||
static int iFld = 20;
|
||||
static int iFld2 = 0 ;
|
||||
static int iArrFld[] = new int[50];
|
||||
static float fArrFld[] = new float[50];
|
||||
|
||||
|
||||
// Only triggers with -XX:-PartialPeelLoop
|
||||
/*
|
||||
* The inner loop is unswitched on (1) which creates a fast and a slow loop that both have (1) removed and instead
|
||||
* (1) is executed before the loop at (3). With the SplitIf optimization we find that (3) dominates (2) in both loops.
|
||||
*
|
||||
* As a result, we can remove (2) from both loops. This, however, has an influence on how the loop tree is built.
|
||||
* Before the SplitIf optimization, the loop tree looks like this:
|
||||
* Loop: N0/N0 has_sfpt
|
||||
* Loop: N338/N314 limit_check profile_predicated predicated counted [0,100),+1 (2 iters) has_sfpt
|
||||
* Loop: N459/N458 profile_predicated predicated counted [0,10000),+1 (5271 iters) has_sfpt (slow loop)
|
||||
* Loop: N343/N267 profile_predicated predicated counted [0,10000),+1 (5271 iters) has_sfpt (fast loop)
|
||||
*
|
||||
* Both unswitched loop have a copy of the skeleton predicate If node that share the same Opaque4 node with its inputs.
|
||||
* The inner loop is never exited normally due to always returning on (4). This means that the branch that exits the
|
||||
* loop on the loop limit check is never taken and has an uncommon trap. Nevertheless, the loop building algorithm still
|
||||
* identifies the fast and the slow loop as children of N338 because of the condition (2) over which the loop is left.
|
||||
* However, after the above mentioned SplitIf optimization the condition (2) is removed from both loops. As a result,
|
||||
* the slow loops (N459) is always exited immediately (x == 100 holds) because the break is executed on the first
|
||||
* iteration of the loop. The loop can be removed (but these nodes are still part of the parent loop N338). The fast loop
|
||||
* (N343), however, is now never exited normally and always returns on the 9800th iteration over (4). The normal loop exit
|
||||
* over the loop limit check is never taken (uncommon trap). Due to the last loop exit (2) being removed, N343 is no longer
|
||||
* recognized as a child loop of N338 due to not having a backedge to the parent loop. The loop tree looks like this:
|
||||
* Loop: N0/N0 has_sfpt
|
||||
* Loop: N338/N314 limit_check profile_predicated predicated counted [0,100),+1 (2 iters) has_sfpt
|
||||
* Loop: N343/N267 profile_predicated predicated counted [0,10000),+1 (5274 iters) has_sfpt
|
||||
*
|
||||
* As a next step, the original parent loop N338 is peeled. The fast and the slow loop still both share skeleton Opaque4 bool
|
||||
* nodes with all its inputs nodes up to and including the OpaqueLoopInit/Stride nodes. These skeleton predicates are still there
|
||||
* even though the slow loop N459 could have been removed (the Opaque4 nodes are only removed after loop opts). Let's look at one
|
||||
* of the skeleton If nodes for the fast loop that uses such a Opaque4 node. The skeleton 'If' is no longer part of the original
|
||||
* parent loop and is therefore not peeled. But now we need some phi nodes to select the correct nodes either from the peeled
|
||||
* iteration or from N338 for this skeleton If of the fast loop. This is done in PhaseIdealLoop::clone_iff() which creates
|
||||
* a new Opaque4 node together with new Bool and Cmp nodes and then inserts some phi nodes to do the selection.
|
||||
*
|
||||
* When afterwards creating pre/main/post loops for the fast loop (N343) that is no child anymore, we find these phi nodes on the
|
||||
* path to the OpaqueLoopInit/Stride nodes which lets the assertion PhaseIdealLoop::skeleton_predicate_has_opaque() fail. These
|
||||
* phi nodes on the path to the OpaqueLoopInit/Stride nodes are unexpected.
|
||||
*
|
||||
* The solution to this problem is to clone the skeleton predicates completely, including clones of all nodes up to and including
|
||||
* the OpaqueLoopInit/Stride nodes (similar to what is done when copying skeleton predicates to the main loop) instead of just
|
||||
* sharing Opaque4 nodes.
|
||||
*/
|
||||
public static int test1() {
|
||||
int i = 0;
|
||||
while (i < 100) {
|
||||
int j = 0;
|
||||
// (3) <new unswitch condition>
|
||||
while (j < 10000) {
|
||||
if (x == 100) { // (1) Loop is unswitched on this condition -> condition shared with (2)
|
||||
y = 34;
|
||||
}
|
||||
|
||||
intArr[idx] = 34;
|
||||
intArr[2*j + 35] = 45;
|
||||
|
||||
if (x == 100) { // (2)
|
||||
y = 35;
|
||||
break;
|
||||
}
|
||||
if (j == 9800) { // (4)
|
||||
return 2;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
intArr[i] = 45;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
// Only triggers with -XX:-PartialPeelLoop
|
||||
public static int test2() {
|
||||
int i = 0;
|
||||
while (i < 100) {
|
||||
int j = 0;
|
||||
while (j < 10000) {
|
||||
if (x == 100) {
|
||||
y = 34;
|
||||
}
|
||||
|
||||
intArr[2*j + 35] = 45;
|
||||
|
||||
if (x == 100) {
|
||||
y = 35;
|
||||
break;
|
||||
}
|
||||
if (j == 9800) {
|
||||
return 2;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
intArr[i] = 45;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
// Only triggers with -XX:-PartialPeelLoop
|
||||
public static int test3() {
|
||||
int i = 0;
|
||||
while (i < 100) {
|
||||
int j = 0;
|
||||
while (j < 10000) {
|
||||
if (x == 100) {
|
||||
y = 34;
|
||||
}
|
||||
|
||||
intArr[idx] = 34;
|
||||
intArr[2*j + 35] = 45;
|
||||
|
||||
if (x == 100) {
|
||||
y = 35;
|
||||
break;
|
||||
}
|
||||
if (j == 9800) {
|
||||
return 2;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return y;
|
||||
}
|
||||
|
||||
// Test that has two loop headers for a single loop (limitation of type flow, see JDK-8255663)
|
||||
// which also triggers the assertion failure of this bug.
|
||||
public static void test4() {
|
||||
int unused = 500; // Required, even though unused
|
||||
boolean b = true;
|
||||
int i = 1;
|
||||
while (++i < 35) {
|
||||
iArrFld[i] = 6;
|
||||
switch (iFld2) {
|
||||
case 40:
|
||||
if (b) {
|
||||
continue;
|
||||
}
|
||||
b = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test that has two loop headers for a single loop (limitation of type flow, see JDK-8255663)
|
||||
// which also triggers the assertion failure of this bug. Only triggers with -XX:-PartialPeelLoop.
|
||||
public static void test5() {
|
||||
int j = 50;
|
||||
int i = 1;
|
||||
while (++i < 40) {
|
||||
j = 5;
|
||||
do {
|
||||
fArrFld[i] = 46;
|
||||
iFld = 5;
|
||||
if (bFld) break;
|
||||
} while (++j < 5);
|
||||
j = 2;
|
||||
do {
|
||||
try {
|
||||
iFld = 56;
|
||||
} catch (ArithmeticException a_e) {}
|
||||
if (bFld) break;
|
||||
} while (++j < 2);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] strArr) {
|
||||
for (int i = 0; i < 5000; i++) {
|
||||
test1();
|
||||
test2();
|
||||
test3();
|
||||
x++;
|
||||
x = x % 106;
|
||||
}
|
||||
test4();
|
||||
test5();
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user