8278228: C2: Improve identical back-to-back if elimination

Reviewed-by: chagedorn, kvn
This commit is contained in:
Roland Westrelin 2022-01-10 07:52:18 +00:00
parent e14fb4f4aa
commit 8d1a1e83f4
8 changed files with 221 additions and 63 deletions

View File

@ -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;

View File

@ -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());
}
}
}

View File

@ -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();

View File

@ -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);
};

View File

@ -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;

View File

@ -31,11 +31,9 @@
//------------------------------split_thru_region------------------------------
// Split Node 'n' through merge point.
Node *PhaseIdealLoop::split_thru_region( Node *n, Node *region ) {
uint wins = 0;
RegionNode* PhaseIdealLoop::split_thru_region(Node* n, RegionNode* region) {
assert(n->is_CFG(), "");
assert( region->is_Region(), "" );
Node *r = new RegionNode( region->req() );
RegionNode* r = new RegionNode(region->req());
IdealLoopTree* loop = get_loop(n);
for (uint i = 1; i < region->req(); i++) {
Node* x = n->clone();
@ -43,9 +41,10 @@ Node *PhaseIdealLoop::split_thru_region( Node *n, Node *region ) {
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 )
if (get_ctrl(in) == region) {
x->set_req(j, in->in(i));
}
}
_igvn.register_new_node_with_optimizer(x);
set_loop(x, loop);
set_idom(x, x->in(0), dom_depth(x->in(0))+1);
@ -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

View File

@ -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);
}
}

View File

@ -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;