8278228: C2: Improve identical back-to-back if elimination
Reviewed-by: chagedorn, kvn
This commit is contained in:
parent
e14fb4f4aa
commit
8d1a1e83f4
@ -1367,7 +1367,7 @@ bool PhaseIdealLoop::loop_predication_impl_helper(IdealLoopTree *loop, ProjNode*
|
||||
invar.map_ctrl(proj, new_predicate_proj); // so that invariance test can be appropriate
|
||||
|
||||
// Eliminate the old If in the loop body
|
||||
dominated_by( new_predicate_proj, iff, proj->_con != new_predicate_proj->_con );
|
||||
dominated_by( new_predicate_proj->as_IfProj(), iff, proj->_con != new_predicate_proj->_con );
|
||||
|
||||
C->set_major_progress();
|
||||
return true;
|
||||
|
@ -532,7 +532,7 @@ void PhaseIdealLoop::peeled_dom_test_elim(IdealLoopTree* loop, Node_List& old_ne
|
||||
if (n->is_If() && n->in(1) == test_cond) {
|
||||
// IfNode was dominated by version in peeled loop body
|
||||
progress = true;
|
||||
dominated_by(old_new[prev->_idx], n);
|
||||
dominated_by(old_new[prev->_idx]->as_IfProj(), n->as_If());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,11 +213,11 @@ void PhaseIdealLoop::do_unswitching(IdealLoopTree *loop, Node_List &old_new) {
|
||||
|
||||
// Hardwire the control paths in the loops into if(true) and if(false)
|
||||
_igvn.rehash_node_delayed(unswitch_iff);
|
||||
dominated_by(proj_true, unswitch_iff, false, false);
|
||||
dominated_by(proj_true->as_IfProj(), unswitch_iff, false, false);
|
||||
|
||||
IfNode* unswitch_iff_clone = old_new[unswitch_iff->_idx]->as_If();
|
||||
_igvn.rehash_node_delayed(unswitch_iff_clone);
|
||||
dominated_by(proj_false, unswitch_iff_clone, false, false);
|
||||
dominated_by(proj_false->as_IfProj(), unswitch_iff_clone, false, false);
|
||||
|
||||
// Reoptimize loops
|
||||
loop->record_for_igvn();
|
||||
|
@ -1471,15 +1471,15 @@ public:
|
||||
Node *has_local_phi_input( Node *n );
|
||||
// Mark an IfNode as being dominated by a prior test,
|
||||
// without actually altering the CFG (and hence IDOM info).
|
||||
void dominated_by( Node *prevdom, Node *iff, bool flip = false, bool exclude_loop_predicate = false );
|
||||
void dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip = false, bool exclude_loop_predicate = false);
|
||||
|
||||
// Split Node 'n' through merge point
|
||||
Node *split_thru_region( Node *n, Node *region );
|
||||
RegionNode* split_thru_region(Node* n, RegionNode* region);
|
||||
// Split Node 'n' through merge point if there is enough win.
|
||||
Node *split_thru_phi( Node *n, Node *region, int policy );
|
||||
// Found an If getting its condition-code input from a Phi in the
|
||||
// same block. Split thru the Region.
|
||||
void do_split_if( Node *iff );
|
||||
void do_split_if(Node *iff, RegionNode** new_false_region = NULL, RegionNode** new_true_region = NULL);
|
||||
|
||||
// Conversion of fill/copy patterns into intrinsic versions
|
||||
bool do_intrinsify_fill();
|
||||
@ -1654,6 +1654,9 @@ public:
|
||||
|
||||
void strip_mined_nest_back_to_counted_loop(IdealLoopTree* loop, const BaseCountedLoopNode* head, Node* back_control,
|
||||
IfNode*&exit_test, SafePointNode*&safepoint);
|
||||
void push_pinned_nodes_thru_region(IfNode* dom_if, Node* region);
|
||||
|
||||
bool try_merge_identical_ifs(Node* n);
|
||||
};
|
||||
|
||||
|
||||
|
@ -229,11 +229,10 @@ Node* PhaseIdealLoop::split_thru_phi(Node* n, Node* region, int policy) {
|
||||
// Replace the dominated test with an obvious true or false. Place it on the
|
||||
// IGVN worklist for later cleanup. Move control-dependent data Nodes on the
|
||||
// live path up to the dominating control.
|
||||
void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff, bool flip, bool exclude_loop_predicate ) {
|
||||
void PhaseIdealLoop::dominated_by(IfProjNode* prevdom, IfNode* iff, bool flip, bool exclude_loop_predicate) {
|
||||
if (VerifyLoopOptimizations && PrintOpto) { tty->print_cr("dominating test"); }
|
||||
|
||||
// prevdom is the dominating projection of the dominating test.
|
||||
assert(iff->is_If(), "must be");
|
||||
assert(iff->Opcode() == Op_If ||
|
||||
iff->Opcode() == Op_CountedLoopEnd ||
|
||||
iff->Opcode() == Op_LongCountedLoopEnd ||
|
||||
@ -263,7 +262,7 @@ void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff, bool flip, bool exc
|
||||
// Make control-dependent data Nodes on the live path (path that will remain
|
||||
// once the dominated IF is removed) become control-dependent on the
|
||||
// dominating projection.
|
||||
Node* dp = iff->as_If()->proj_out_or_null(pop == Op_IfTrue);
|
||||
Node* dp = iff->proj_out_or_null(pop == Op_IfTrue);
|
||||
|
||||
// Loop predicates may have depending checks which should not
|
||||
// be skipped. For example, range check predicate has two checks
|
||||
@ -272,7 +271,7 @@ void PhaseIdealLoop::dominated_by( Node *prevdom, Node *iff, bool flip, bool exc
|
||||
return;
|
||||
|
||||
ProjNode* dp_proj = dp->as_Proj();
|
||||
ProjNode* unc_proj = iff->as_If()->proj_out(1 - dp_proj->_con)->as_Proj();
|
||||
ProjNode* unc_proj = iff->proj_out(1 - dp_proj->_con)->as_Proj();
|
||||
if (exclude_loop_predicate &&
|
||||
(unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_predicate) != NULL ||
|
||||
unc_proj->is_uncommon_trap_proj(Deoptimization::Reason_profile_predicate) != NULL ||
|
||||
@ -1175,15 +1174,6 @@ bool PhaseIdealLoop::identical_backtoback_ifs(Node *n) {
|
||||
return false;
|
||||
}
|
||||
|
||||
IfNode* n_if = n->as_If();
|
||||
if (n_if->proj_out(0)->outcnt() > 1 || n_if->proj_out(1)->outcnt() > 1) {
|
||||
// Removing the dominated If node by using the split-if optimization does not work if there are data dependencies.
|
||||
// Some data dependencies depend on its immediate dominator If node and should not be separated from it (e.g. null
|
||||
// checks, division by zero checks etc.). Bail out for now until data dependencies are correctly handled when
|
||||
// optimizing back-to-back ifs.
|
||||
return false;
|
||||
}
|
||||
|
||||
Node* region = n->in(0);
|
||||
Node* dom = idom(region);
|
||||
if (!dom->is_If() || dom->in(1) != n->in(1)) {
|
||||
@ -1359,28 +1349,7 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
|
||||
}
|
||||
|
||||
// Two identical ifs back to back can be merged
|
||||
if (identical_backtoback_ifs(n) && can_split_if(n->in(0))) {
|
||||
Node *n_ctrl = n->in(0);
|
||||
PhiNode* bolphi = PhiNode::make_blank(n_ctrl, n->in(1));
|
||||
IfNode* dom_if = idom(n_ctrl)->as_If();
|
||||
Node* proj_true = dom_if->proj_out(1);
|
||||
Node* proj_false = dom_if->proj_out(0);
|
||||
Node* con_true = _igvn.makecon(TypeInt::ONE);
|
||||
Node* con_false = _igvn.makecon(TypeInt::ZERO);
|
||||
|
||||
for (uint i = 1; i < n_ctrl->req(); i++) {
|
||||
if (is_dominator(proj_true, n_ctrl->in(i))) {
|
||||
bolphi->init_req(i, con_true);
|
||||
} else {
|
||||
assert(is_dominator(proj_false, n_ctrl->in(i)), "bad if");
|
||||
bolphi->init_req(i, con_false);
|
||||
}
|
||||
}
|
||||
register_new_node(bolphi, n_ctrl);
|
||||
_igvn.replace_input_of(n, 1, bolphi);
|
||||
|
||||
// Now split the IF
|
||||
do_split_if(n);
|
||||
if (try_merge_identical_ifs(n)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1416,7 +1385,7 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
|
||||
// Replace the dominated test with an obvious true or false.
|
||||
// Place it on the IGVN worklist for later cleanup.
|
||||
C->set_major_progress();
|
||||
dominated_by(prevdom, n, false, true);
|
||||
dominated_by(prevdom->as_IfProj(), n->as_If(), false, true);
|
||||
#ifndef PRODUCT
|
||||
if( VerifyLoopOptimizations ) verify();
|
||||
#endif
|
||||
@ -1441,6 +1410,118 @@ void PhaseIdealLoop::split_if_with_blocks_post(Node *n) {
|
||||
}
|
||||
}
|
||||
|
||||
// Tranform:
|
||||
//
|
||||
// if (some_condition) {
|
||||
// // body 1
|
||||
// } else {
|
||||
// // body 2
|
||||
// }
|
||||
// if (some_condition) {
|
||||
// // body 3
|
||||
// } else {
|
||||
// // body 4
|
||||
// }
|
||||
//
|
||||
// into:
|
||||
//
|
||||
//
|
||||
// if (some_condition) {
|
||||
// // body 1
|
||||
// // body 3
|
||||
// } else {
|
||||
// // body 2
|
||||
// // body 4
|
||||
// }
|
||||
bool PhaseIdealLoop::try_merge_identical_ifs(Node* n) {
|
||||
if (identical_backtoback_ifs(n) && can_split_if(n->in(0))) {
|
||||
Node *n_ctrl = n->in(0);
|
||||
IfNode* dom_if = idom(n_ctrl)->as_If();
|
||||
ProjNode* dom_proj_true = dom_if->proj_out(1);
|
||||
ProjNode* dom_proj_false = dom_if->proj_out(0);
|
||||
|
||||
// Now split the IF
|
||||
RegionNode* new_false_region;
|
||||
RegionNode* new_true_region;
|
||||
do_split_if(n, &new_false_region, &new_true_region);
|
||||
assert(new_false_region->req() == new_true_region->req(), "");
|
||||
#ifdef ASSERT
|
||||
for (uint i = 1; i < new_false_region->req(); ++i) {
|
||||
assert(new_false_region->in(i)->in(0) == new_true_region->in(i)->in(0), "unexpected shape following split if");
|
||||
assert(i == new_false_region->req() - 1 || new_false_region->in(i)->in(0)->in(1) == new_false_region->in(i + 1)->in(0)->in(1), "unexpected shape following split if");
|
||||
}
|
||||
#endif
|
||||
assert(new_false_region->in(1)->in(0)->in(1) == dom_if->in(1), "dominating if and dominated if after split must share test");
|
||||
|
||||
// We now have:
|
||||
// if (some_condition) {
|
||||
// // body 1
|
||||
// if (some_condition) {
|
||||
// body3: // new_true_region
|
||||
// // body3
|
||||
// } else {
|
||||
// goto body4;
|
||||
// }
|
||||
// } else {
|
||||
// // body 2
|
||||
// if (some_condition) {
|
||||
// goto body3;
|
||||
// } else {
|
||||
// body4: // new_false_region
|
||||
// // body4;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
|
||||
// clone pinned nodes thru the resulting regions
|
||||
push_pinned_nodes_thru_region(dom_if, new_true_region);
|
||||
push_pinned_nodes_thru_region(dom_if, new_false_region);
|
||||
|
||||
// Optimize out the cloned ifs. Because pinned nodes were cloned, this also allows a CastPP that would be dependent
|
||||
// on a projection of n to have the dom_if as a control dependency. We don't want the CastPP to end up with an
|
||||
// unrelated control dependency.
|
||||
for (uint i = 1; i < new_false_region->req(); i++) {
|
||||
if (is_dominator(dom_proj_true, new_false_region->in(i))) {
|
||||
dominated_by(dom_proj_true->as_IfProj(), new_false_region->in(i)->in(0)->as_If(), false, false);
|
||||
} else {
|
||||
assert(is_dominator(dom_proj_false, new_false_region->in(i)), "bad if");
|
||||
dominated_by(dom_proj_false->as_IfProj(), new_false_region->in(i)->in(0)->as_If(), false, false);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void PhaseIdealLoop::push_pinned_nodes_thru_region(IfNode* dom_if, Node* region) {
|
||||
for (DUIterator i = region->outs(); region->has_out(i); i++) {
|
||||
Node* u = region->out(i);
|
||||
if (!has_ctrl(u) || u->is_Phi() || !u->depends_only_on_test() || !_igvn.no_dependent_zero_check(u)) {
|
||||
continue;
|
||||
}
|
||||
assert(u->in(0) == region, "not a control dependent node?");
|
||||
uint j = 1;
|
||||
for (; j < u->req(); ++j) {
|
||||
Node* in = u->in(j);
|
||||
if (!is_dominator(ctrl_or_self(in), dom_if)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (j == u->req()) {
|
||||
Node *phi = PhiNode::make_blank(region, u);
|
||||
for (uint k = 1; k < region->req(); ++k) {
|
||||
Node* clone = u->clone();
|
||||
clone->set_req(0, region->in(k));
|
||||
register_new_node(clone, region->in(k));
|
||||
phi->init_req(k, clone);
|
||||
}
|
||||
register_new_node(phi, region);
|
||||
_igvn.replace_node(u, phi);
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool PhaseIdealLoop::safe_for_if_replacement(const Node* dom) const {
|
||||
if (!dom->is_CountedLoopEnd()) {
|
||||
return true;
|
||||
|
@ -31,20 +31,19 @@
|
||||
|
||||
//------------------------------split_thru_region------------------------------
|
||||
// Split Node 'n' through merge point.
|
||||
Node *PhaseIdealLoop::split_thru_region( Node *n, Node *region ) {
|
||||
uint wins = 0;
|
||||
assert( n->is_CFG(), "" );
|
||||
assert( region->is_Region(), "" );
|
||||
Node *r = new RegionNode( region->req() );
|
||||
IdealLoopTree *loop = get_loop( n );
|
||||
for( uint i = 1; i < region->req(); i++ ) {
|
||||
Node *x = n->clone();
|
||||
Node *in0 = n->in(0);
|
||||
if( in0->in(0) == region ) x->set_req( 0, in0->in(i) );
|
||||
for( uint j = 1; j < n->req(); j++ ) {
|
||||
Node *in = n->in(j);
|
||||
if( get_ctrl(in) == region )
|
||||
x->set_req( j, in->in(i) );
|
||||
RegionNode* PhaseIdealLoop::split_thru_region(Node* n, RegionNode* region) {
|
||||
assert(n->is_CFG(), "");
|
||||
RegionNode* r = new RegionNode(region->req());
|
||||
IdealLoopTree* loop = get_loop(n);
|
||||
for (uint i = 1; i < region->req(); i++) {
|
||||
Node* x = n->clone();
|
||||
Node* in0 = n->in(0);
|
||||
if (in0->in(0) == region) x->set_req(0, in0->in(i));
|
||||
for (uint j = 1; j < n->req(); j++) {
|
||||
Node* in = n->in(j);
|
||||
if (get_ctrl(in) == region) {
|
||||
x->set_req(j, in->in(i));
|
||||
}
|
||||
}
|
||||
_igvn.register_new_node_with_optimizer(x);
|
||||
set_loop(x, loop);
|
||||
@ -56,8 +55,9 @@ Node *PhaseIdealLoop::split_thru_region( Node *n, Node *region ) {
|
||||
r->set_req(0,region); // Not a TRUE RegionNode
|
||||
_igvn.register_new_node_with_optimizer(r);
|
||||
set_loop(r, loop);
|
||||
if( !loop->_child )
|
||||
if (!loop->_child) {
|
||||
loop->_body.push(r);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
@ -433,7 +433,7 @@ void PhaseIdealLoop::handle_use( Node *use, Node *def, small_cache *cache, Node
|
||||
//------------------------------do_split_if------------------------------------
|
||||
// Found an If getting its condition-code input from a Phi in the same block.
|
||||
// Split thru the Region.
|
||||
void PhaseIdealLoop::do_split_if( Node *iff ) {
|
||||
void PhaseIdealLoop::do_split_if(Node* iff, RegionNode** new_false_region, RegionNode** new_true_region) {
|
||||
if (PrintOpto && VerifyLoopOptimizations) {
|
||||
tty->print_cr("Split-if");
|
||||
}
|
||||
@ -442,7 +442,7 @@ void PhaseIdealLoop::do_split_if( Node *iff ) {
|
||||
}
|
||||
|
||||
C->set_major_progress();
|
||||
Node *region = iff->in(0);
|
||||
RegionNode *region = iff->in(0)->as_Region();
|
||||
Node *region_dom = idom(region);
|
||||
|
||||
// We are going to clone this test (and the control flow with it) up through
|
||||
@ -491,17 +491,18 @@ void PhaseIdealLoop::do_split_if( Node *iff ) {
|
||||
|
||||
// Now we have no instructions in the block containing the IF.
|
||||
// Split the IF.
|
||||
Node *new_iff = split_thru_region( iff, region );
|
||||
RegionNode *new_iff = split_thru_region(iff, region);
|
||||
|
||||
// Replace both uses of 'new_iff' with Regions merging True/False
|
||||
// paths. This makes 'new_iff' go dead.
|
||||
Node *old_false = NULL, *old_true = NULL;
|
||||
Node *new_false = NULL, *new_true = NULL;
|
||||
RegionNode* new_false = NULL;
|
||||
RegionNode* new_true = NULL;
|
||||
for (DUIterator_Last j2min, j2 = iff->last_outs(j2min); j2 >= j2min; --j2) {
|
||||
Node *ifp = iff->last_out(j2);
|
||||
assert( ifp->Opcode() == Op_IfFalse || ifp->Opcode() == Op_IfTrue, "" );
|
||||
ifp->set_req(0, new_iff);
|
||||
Node *ifpx = split_thru_region( ifp, region );
|
||||
RegionNode* ifpx = split_thru_region(ifp, region);
|
||||
|
||||
// Replace 'If' projection of a Region with a Region of
|
||||
// 'If' projections.
|
||||
@ -577,6 +578,13 @@ void PhaseIdealLoop::do_split_if( Node *iff ) {
|
||||
|
||||
_igvn.remove_dead_node(region);
|
||||
|
||||
if (new_false_region != NULL) {
|
||||
*new_false_region = new_false;
|
||||
}
|
||||
if (new_true_region != NULL) {
|
||||
*new_true_region = new_true;
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
if( VerifyLoopOptimizations ) verify();
|
||||
#endif
|
||||
|
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* Copyright (c) 2022, 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.
|
||||
*/
|
||||
|
||||
package compiler.c2.irTests;
|
||||
|
||||
import compiler.lib.ir_framework.*;
|
||||
import jdk.test.lib.Utils;
|
||||
import java.util.Random;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8278228
|
||||
* @summary C2: Improve identical back-to-back if elimination
|
||||
* @library /test/lib /
|
||||
* @run driver compiler.c2.irTests.TestBackToBackIfs
|
||||
*/
|
||||
|
||||
public class TestBackToBackIfs {
|
||||
public static void main(String[] args) {
|
||||
TestFramework.run();
|
||||
}
|
||||
|
||||
static private int int_field;
|
||||
|
||||
@Test
|
||||
@IR(counts = { IRNode.IF, "1" })
|
||||
public static void test(int a, int b) {
|
||||
if (a == b) {
|
||||
int_field = 0x42;
|
||||
} else {
|
||||
int_field = 42;
|
||||
}
|
||||
if (a == b) {
|
||||
int_field = 0x42;
|
||||
} else {
|
||||
int_field = 42;
|
||||
}
|
||||
}
|
||||
|
||||
@Run(test = "test")
|
||||
public static void test_runner() {
|
||||
test(42, 0x42);
|
||||
test(0x42, 0x42);
|
||||
}
|
||||
}
|
@ -115,6 +115,7 @@ public class IRNode {
|
||||
public static final String LOOP = START + "Loop" + MID + END;
|
||||
public static final String COUNTEDLOOP = START + "CountedLoop\\b" + MID + END;
|
||||
public static final String COUNTEDLOOP_MAIN = START + "CountedLoop\\b" + MID + "main" + END;
|
||||
public static final String IF = START + "If\\b" + MID + END;
|
||||
|
||||
public static final String CALL = START + "Call.*Java" + MID + END;
|
||||
public static final String CALL_OF_METHOD = COMPOSITE_PREFIX + START + "Call.*Java" + MID + IS_REPLACED + " " + END;
|
||||
|
Loading…
x
Reference in New Issue
Block a user