8191887: assert(b->is_Bool()) in PhaseIdealLoop::clone_iff() due to Opaque4 node

Add special handling for graph shape If->Opaque4->Bool->CmpP

Reviewed-by: kvn
This commit is contained in:
Roland Westrelin 2017-11-27 16:05:11 -08:00
parent 41b6561e52
commit eb10d407d3
3 changed files with 167 additions and 45 deletions
src/hotspot/share/opto
test/hotspot/jtreg/compiler/unsafe

@ -1098,7 +1098,7 @@ public:
// "Nearly" because all Nodes have been cloned from the original in the loop,
// but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs
// through the Phi recursively, and return a Bool.
BoolNode *clone_iff( PhiNode *phi, IdealLoopTree *loop );
Node *clone_iff( PhiNode *phi, IdealLoopTree *loop );
CmpNode *clone_bool( PhiNode *phi, IdealLoopTree *loop );

@ -1407,47 +1407,56 @@ void PhaseIdealLoop::split_if_with_blocks( VectorSet &visited, Node_Stack &nstac
// "Nearly" because all Nodes have been cloned from the original in the loop,
// but the fall-in edges to the Cmp are different. Clone bool/Cmp pairs
// through the Phi recursively, and return a Bool.
BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) {
Node* PhaseIdealLoop::clone_iff(PhiNode *phi, IdealLoopTree *loop) {
// Convert this Phi into a Phi merging Bools
uint i;
for( i = 1; i < phi->req(); i++ ) {
for (i = 1; i < phi->req(); i++) {
Node *b = phi->in(i);
if( b->is_Phi() ) {
_igvn.replace_input_of(phi, i, clone_iff( b->as_Phi(), loop ));
if (b->is_Phi()) {
_igvn.replace_input_of(phi, i, clone_iff(b->as_Phi(), loop));
} else {
assert( b->is_Bool(), "" );
assert(b->is_Bool() || b->Opcode() == Op_Opaque4, "");
}
}
Node *sample_bool = phi->in(1);
Node *sample_cmp = sample_bool->in(1);
Node* n = phi->in(1);
Node* sample_opaque = NULL;
Node *sample_bool = NULL;
if (n->Opcode() == Op_Opaque4) {
sample_opaque = n;
sample_bool = n->in(1);
assert(sample_bool->is_Bool(), "wrong type");
} else {
sample_bool = n;
}
Node *sample_cmp = sample_bool->in(1);
// Make Phis to merge the Cmp's inputs.
PhiNode *phi1 = new PhiNode( phi->in(0), Type::TOP );
PhiNode *phi2 = new PhiNode( phi->in(0), Type::TOP );
for( i = 1; i < phi->req(); i++ ) {
Node *n1 = phi->in(i)->in(1)->in(1);
Node *n2 = phi->in(i)->in(1)->in(2);
phi1->set_req( i, n1 );
phi2->set_req( i, n2 );
phi1->set_type( phi1->type()->meet_speculative(n1->bottom_type()));
phi2->set_type( phi2->type()->meet_speculative(n2->bottom_type()));
PhiNode *phi1 = new PhiNode(phi->in(0), Type::TOP);
PhiNode *phi2 = new PhiNode(phi->in(0), Type::TOP);
for (i = 1; i < phi->req(); i++) {
Node *n1 = sample_opaque == NULL ? phi->in(i)->in(1)->in(1) : phi->in(i)->in(1)->in(1)->in(1);
Node *n2 = sample_opaque == NULL ? phi->in(i)->in(1)->in(2) : phi->in(i)->in(1)->in(1)->in(2);
phi1->set_req(i, n1);
phi2->set_req(i, n2);
phi1->set_type(phi1->type()->meet_speculative(n1->bottom_type()));
phi2->set_type(phi2->type()->meet_speculative(n2->bottom_type()));
}
// See if these Phis have been made before.
// Register with optimizer
Node *hit1 = _igvn.hash_find_insert(phi1);
if( hit1 ) { // Hit, toss just made Phi
if (hit1) { // Hit, toss just made Phi
_igvn.remove_dead_node(phi1); // Remove new phi
assert( hit1->is_Phi(), "" );
assert(hit1->is_Phi(), "" );
phi1 = (PhiNode*)hit1; // Use existing phi
} else { // Miss
_igvn.register_new_node_with_optimizer(phi1);
}
Node *hit2 = _igvn.hash_find_insert(phi2);
if( hit2 ) { // Hit, toss just made Phi
if (hit2) { // Hit, toss just made Phi
_igvn.remove_dead_node(phi2); // Remove new phi
assert( hit2->is_Phi(), "" );
assert(hit2->is_Phi(), "" );
phi2 = (PhiNode*)hit2; // Use existing phi
} else { // Miss
_igvn.register_new_node_with_optimizer(phi2);
@ -1457,8 +1466,8 @@ BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) {
set_ctrl(phi2, phi->in(0));
// Make a new Cmp
Node *cmp = sample_cmp->clone();
cmp->set_req( 1, phi1 );
cmp->set_req( 2, phi2 );
cmp->set_req(1, phi1);
cmp->set_req(2, phi2);
_igvn.register_new_node_with_optimizer(cmp);
set_ctrl(cmp, phi->in(0));
@ -1468,8 +1477,16 @@ BoolNode *PhaseIdealLoop::clone_iff( PhiNode *phi, IdealLoopTree *loop ) {
_igvn.register_new_node_with_optimizer(b);
set_ctrl(b, phi->in(0));
assert( b->is_Bool(), "" );
return (BoolNode*)b;
if (sample_opaque != NULL) {
Node* opaque = sample_opaque->clone();
opaque->set_req(1, b);
_igvn.register_new_node_with_optimizer(opaque);
set_ctrl(opaque, phi->in(0));
return opaque;
}
assert(b->is_Bool(), "");
return b;
}
//------------------------------clone_bool-------------------------------------
@ -1749,21 +1766,24 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
// in the loop to break the loop, then test is again outside of the
// loop to determine which way the loop exited.
// Loop predicate If node connects to Bool node through Opaque1 node.
if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use)) {
if (use->is_If() || use->is_CMove() || C->is_predicate_opaq(use) || use->Opcode() == Op_Opaque4) {
// Since this code is highly unlikely, we lazily build the worklist
// of such Nodes to go split.
if( !split_if_set )
if (!split_if_set) {
split_if_set = new Node_List(area);
}
split_if_set->push(use);
}
if( use->is_Bool() ) {
if( !split_bool_set )
if (use->is_Bool()) {
if (!split_bool_set) {
split_bool_set = new Node_List(area);
}
split_bool_set->push(use);
}
if( use->Opcode() == Op_CreateEx ) {
if( !split_cex_set )
if (use->Opcode() == Op_CreateEx) {
if (!split_cex_set) {
split_cex_set = new Node_List(area);
}
split_cex_set->push(use);
}
@ -1852,31 +1872,31 @@ void PhaseIdealLoop::clone_loop( IdealLoopTree *loop, Node_List &old_new, int dd
// takes control from one or more OLD Regions (which in turn get from NEW
// Regions). In any case, there will be a set of Phis for each merge point
// from the IF up to where the original BOOL def exists the loop.
if( split_if_set ) {
while( split_if_set->size() ) {
if (split_if_set) {
while (split_if_set->size()) {
Node *iff = split_if_set->pop();
if( iff->in(1)->is_Phi() ) {
BoolNode *b = clone_iff( iff->in(1)->as_Phi(), loop );
if (iff->in(1)->is_Phi()) {
Node *b = clone_iff(iff->in(1)->as_Phi(), loop);
_igvn.replace_input_of(iff, 1, b);
}
}
}
if( split_bool_set ) {
while( split_bool_set->size() ) {
if (split_bool_set) {
while (split_bool_set->size()) {
Node *b = split_bool_set->pop();
Node *phi = b->in(1);
assert( phi->is_Phi(), "" );
CmpNode *cmp = clone_bool( (PhiNode*)phi, loop );
assert(phi->is_Phi(), "");
CmpNode *cmp = clone_bool((PhiNode*)phi, loop);
_igvn.replace_input_of(b, 1, cmp);
}
}
if( split_cex_set ) {
while( split_cex_set->size() ) {
if (split_cex_set) {
while (split_cex_set->size()) {
Node *b = split_cex_set->pop();
assert( b->in(0)->is_Region(), "" );
assert( b->in(1)->is_Phi(), "" );
assert( b->in(0)->in(0) == b->in(1)->in(0), "" );
split_up( b, b->in(0), NULL );
assert(b->in(0)->is_Region(), "");
assert(b->in(1)->is_Phi(), "");
assert(b->in(0)->in(0) == b->in(1)->in(0), "");
split_up(b, b->in(0), NULL);
}
}

@ -0,0 +1,102 @@
/*
* Copyright (c) 2017, Red Hat, Inc. 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 8191887
* @summary loop cloning misses support for Opaque4 node
* @modules java.base/jdk.internal.misc:+open
*
* @run main/othervm -XX:-BackgroundCompilation TestLoopUnswitching
*
*/
import jdk.internal.misc.Unsafe;
import java.lang.reflect.Field;
import java.util.Arrays;
public class TestLoopUnswitching {
static final jdk.internal.misc.Unsafe UNSAFE = Unsafe.getUnsafe();
static final long F_OFFSET;
static class A {
int f;
A(int f) {
this.f = f;
}
}
static {
try {
Field fField = A.class.getDeclaredField("f");
F_OFFSET = UNSAFE.objectFieldOffset(fField);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static int test1(A[] arr, boolean flag1, boolean flag2) {
int res = 0;
for (int i = 0; i < 10; i++) {
A a = arr[i];
if (flag1) { // triggers unswitching
res += UNSAFE.getInt(a, F_OFFSET);
}
if (flag2) {
// Opaque4 node here is in the loop but If is out of the loop
res += UNSAFE.getInt(a, F_OFFSET);
break;
}
res += UNSAFE.getInt(a, F_OFFSET);
}
return res;
}
static int test2(A[] arr, boolean flag1, boolean flag2) {
int res = 0;
for (int i = 0; i < 10; i++) {
A a = arr[i];
if (flag1) { // triggers unswitching
res += UNSAFE.getInt(a, F_OFFSET);
}
if (flag2) {
// Opaque4 node here is out of the loop but Bool is in the the loop
res += UNSAFE.getInt(a, F_OFFSET);
break;
}
res += a.f;
}
return res;
}
static public void main(String[] args) {
A[] arr = new A[1000];
Arrays.fill(arr, new A(0x42));
for (int i = 0; i < 20000; i++) {
test1(arr, (i%2) == 0, (i%2) == 0);
test2(arr, (i%2) == 0, (i%2) == 0);
}
}
}