Skip to content

Commit 2dc930d

Browse files
committedSep 4, 2023
8314997: Missing optimization opportunities due to missing try_clean_mem_phi() calls
Reviewed-by: roland, kvn, thartmann
1 parent ab12c5d commit 2dc930d

File tree

3 files changed

+230
-58
lines changed

3 files changed

+230
-58
lines changed
 

‎src/hotspot/share/opto/cfgnode.cpp

+100-56
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ void RegionNode::verify_can_be_irreducible_entry() const {
447447
}
448448
#endif //ASSERT
449449

450-
bool RegionNode::try_clean_mem_phi(PhaseGVN *phase) {
450+
void RegionNode::try_clean_mem_phis(PhaseIterGVN* igvn) {
451451
// Incremental inlining + PhaseStringOpts sometimes produce:
452452
//
453453
// cmpP with 1 top input
@@ -464,32 +464,60 @@ bool RegionNode::try_clean_mem_phi(PhaseGVN *phase) {
464464
// replaced by If's control input but because there's still a Phi,
465465
// the Region stays in the graph. The top input from the cmpP is
466466
// propagated forward and a subgraph that is useful goes away. The
467-
// code below replaces the Phi with the MergeMem so that the Region
468-
// is simplified.
469-
470-
PhiNode* phi = has_unique_phi();
471-
if (phi && phi->type() == Type::MEMORY && req() == 3 && phi->is_diamond_phi(true)) {
472-
MergeMemNode* m = nullptr;
473-
assert(phi->req() == 3, "same as region");
474-
for (uint i = 1; i < 3; ++i) {
475-
Node *mem = phi->in(i);
476-
if (mem && mem->is_MergeMem() && in(i)->outcnt() == 1) {
477-
// Nothing is control-dependent on path #i except the region itself.
478-
m = mem->as_MergeMem();
479-
uint j = 3 - i;
480-
Node* other = phi->in(j);
481-
if (other && other == m->base_memory()) {
482-
// m is a successor memory to other, and is not pinned inside the diamond, so push it out.
483-
// This will allow the diamond to collapse completely.
484-
phase->is_IterGVN()->replace_node(phi, m);
485-
return true;
486-
}
487-
}
467+
// code in PhiNode::try_clean_memory_phi() replaces the Phi with the
468+
// MergeMem in order to remove the Region if its last phi dies.
469+
470+
if (!is_diamond()) {
471+
return;
472+
}
473+
474+
for (DUIterator_Fast imax, i = fast_outs(imax); i < imax; i++) {
475+
Node* phi = fast_out(i);
476+
if (phi->is_Phi() && phi->as_Phi()->try_clean_memory_phi(igvn)) {
477+
--i;
478+
--imax;
488479
}
489480
}
490-
return false;
491481
}
492482

483+
// Does this region merge a simple diamond formed by a proper IfNode?
484+
//
485+
// Cmp
486+
// /
487+
// ctrl Bool
488+
// \ /
489+
// IfNode
490+
// / \
491+
// IfFalse IfTrue
492+
// \ /
493+
// Region
494+
bool RegionNode::is_diamond() const {
495+
if (req() != 3) {
496+
return false;
497+
}
498+
499+
Node* left_path = in(1);
500+
Node* right_path = in(2);
501+
if (left_path == nullptr || right_path == nullptr) {
502+
return false;
503+
}
504+
Node* diamond_if = left_path->in(0);
505+
if (diamond_if == nullptr || !diamond_if->is_If() || diamond_if != right_path->in(0)) {
506+
// Not an IfNode merging a diamond or TOP.
507+
return false;
508+
}
509+
510+
// Check for a proper bool/cmp
511+
const Node* bol = diamond_if->in(1);
512+
if (!bol->is_Bool()) {
513+
return false;
514+
}
515+
const Node* cmp = bol->in(1);
516+
if (!cmp->is_Cmp()) {
517+
return false;
518+
}
519+
return true;
520+
}
493521
//------------------------------Ideal------------------------------------------
494522
// Return a node which is more "ideal" than the current node. Must preserve
495523
// the CFG, but we can still strip out dead paths.
@@ -501,10 +529,8 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
501529
// arm of the same IF. If found, then the control-flow split is useless.
502530
bool has_phis = false;
503531
if (can_reshape) { // Need DU info to check for Phi users
532+
try_clean_mem_phis(phase->is_IterGVN());
504533
has_phis = (has_phi() != nullptr); // Cache result
505-
if (has_phis && try_clean_mem_phi(phase)) {
506-
has_phis = false;
507-
}
508534

509535
if (!has_phis) { // No Phi users? Nothing merging?
510536
for (uint i = 1; i < req()-1; i++) {
@@ -1327,42 +1353,60 @@ const Type* PhiNode::Value(PhaseGVN* phase) const {
13271353
return ft;
13281354
}
13291355

1330-
1331-
//------------------------------is_diamond_phi---------------------------------
13321356
// Does this Phi represent a simple well-shaped diamond merge? Return the
13331357
// index of the true path or 0 otherwise.
1334-
// If check_control_only is true, do not inspect the If node at the
1335-
// top, and return -1 (not an edge number) on success.
1336-
int PhiNode::is_diamond_phi(bool check_control_only) const {
1337-
// Check for a 2-path merge
1338-
Node *region = in(0);
1339-
if( !region ) return 0;
1340-
if( region->req() != 3 ) return 0;
1341-
if( req() != 3 ) return 0;
1342-
// Check that both paths come from the same If
1343-
Node *ifp1 = region->in(1);
1344-
Node *ifp2 = region->in(2);
1345-
if( !ifp1 || !ifp2 ) return 0;
1346-
Node *iff = ifp1->in(0);
1347-
if( !iff || !iff->is_If() ) return 0;
1348-
if( iff != ifp2->in(0) ) return 0;
1349-
if (check_control_only) return -1;
1350-
// Check for a proper bool/cmp
1351-
const Node *b = iff->in(1);
1352-
if( !b->is_Bool() ) return 0;
1353-
const Node *cmp = b->in(1);
1354-
if( !cmp->is_Cmp() ) return 0;
1355-
1356-
// Check for branching opposite expected
1357-
if( ifp2->Opcode() == Op_IfTrue ) {
1358-
assert( ifp1->Opcode() == Op_IfFalse, "" );
1359-
return 2;
1360-
} else {
1361-
assert( ifp1->Opcode() == Op_IfTrue, "" );
1358+
int PhiNode::is_diamond_phi() const {
1359+
Node* region = in(0);
1360+
assert(region != nullptr && region->is_Region(), "phi must have region");
1361+
if (!region->as_Region()->is_diamond()) {
1362+
return 0;
1363+
}
1364+
1365+
if (region->in(1)->is_IfTrue()) {
1366+
assert(region->in(2)->is_IfFalse(), "bad If");
13621367
return 1;
1368+
} else {
1369+
// Flipped projections.
1370+
assert(region->in(2)->is_IfTrue(), "bad If");
1371+
return 2;
13631372
}
13641373
}
13651374

1375+
// Do the following transformation if we find the corresponding graph shape, remove the involved memory phi and return
1376+
// true. Otherwise, return false if the transformation cannot be applied.
1377+
//
1378+
// If If
1379+
// / \ / \
1380+
// IfFalse IfTrue /- Some Node IfFalse IfTrue
1381+
// \ / / / \ / Some Node
1382+
// Region / /-MergeMem ===> Region |
1383+
// / \---Phi | MergeMem
1384+
// [other phis] \ [other phis] |
1385+
// use use
1386+
bool PhiNode::try_clean_memory_phi(PhaseIterGVN* igvn) {
1387+
if (_type != Type::MEMORY) {
1388+
return false;
1389+
}
1390+
assert(is_diamond_phi() > 0, "sanity");
1391+
assert(req() == 3, "same as region");
1392+
const Node* region = in(0);
1393+
for (uint i = 1; i < 3; i++) {
1394+
Node* phi_input = in(i);
1395+
if (phi_input != nullptr && phi_input->is_MergeMem() && region->in(i)->outcnt() == 1) {
1396+
// Nothing is control-dependent on path #i except the region itself.
1397+
MergeMemNode* merge_mem = phi_input->as_MergeMem();
1398+
uint j = 3 - i;
1399+
Node* other_phi_input = in(j);
1400+
if (other_phi_input != nullptr && other_phi_input == merge_mem->base_memory()) {
1401+
// merge_mem is a successor memory to other_phi_input, and is not pinned inside the diamond, so push it out.
1402+
// This will allow the diamond to collapse completely if there are no other phis left.
1403+
igvn->replace_node(this, merge_mem);
1404+
return true;
1405+
}
1406+
}
1407+
}
1408+
return false;
1409+
}
13661410
//----------------------------check_cmove_id-----------------------------------
13671411
// Check for CMove'ing a constant after comparing against the constant.
13681412
// Happens all the time now, since if we compare equality vs a constant in

‎src/hotspot/share/opto/cfgnode.hpp

+4-2
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,8 @@ class RegionNode : public Node {
132132
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
133133
void remove_unreachable_subgraph(PhaseIterGVN* igvn);
134134
virtual const RegMask &out_RegMask() const;
135-
bool try_clean_mem_phi(PhaseGVN* phase);
135+
bool is_diamond() const;
136+
void try_clean_mem_phis(PhaseIterGVN* phase);
136137
bool optimize_trichotomy(PhaseIterGVN* igvn);
137138
NOT_PRODUCT(virtual void dump_spec(outputStream* st) const;)
138139
};
@@ -233,7 +234,8 @@ class PhiNode : public TypeNode {
233234
LoopSafety simple_data_loop_check(Node *in) const;
234235
// Is it unsafe data loop? It becomes a dead loop if this phi node removed.
235236
bool is_unsafe_data_reference(Node *in) const;
236-
int is_diamond_phi(bool check_control_only = false) const;
237+
int is_diamond_phi() const;
238+
bool try_clean_memory_phi(PhaseIterGVN* igvn);
237239
virtual int Opcode() const;
238240
virtual bool pinned() const { return in(0) != 0; }
239241
virtual const TypePtr *adr_type() const { verify_adr_type(true); return _adr_type; }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
/*
2+
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
3+
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+
*
5+
* This code is free software; you can redistribute it and/or modify it
6+
* under the terms of the GNU General Public License version 2 only, as
7+
* published by the Free Software Foundation.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
package compiler.c2.irTests.igvn;
24+
25+
import compiler.lib.ir_framework.*;
26+
import jdk.test.lib.Asserts;
27+
28+
/*
29+
* @test
30+
* @bug 8314997
31+
* @requires vm.debug == true & vm.compiler2.enabled
32+
* @summary Test that diamond if-region is removed due to calling try_clean_mem_phi().
33+
* @library /test/lib /
34+
* @run driver compiler.c2.irTests.igvn.TestCleanMemPhi
35+
*/
36+
public class TestCleanMemPhi {
37+
static boolean flag, flag2;
38+
static int iFld;
39+
40+
public static void main(String[] args) {
41+
TestFramework testFramework = new TestFramework();
42+
testFramework.setDefaultWarmup(0);
43+
testFramework.addFlags("-XX:+AlwaysIncrementalInline", "-XX:-PartialPeelLoop", "-XX:-LoopUnswitching");
44+
testFramework.addScenarios(new Scenario(1, "-XX:-StressIGVN"),
45+
new Scenario(2, "-XX:+StressIGVN"));
46+
testFramework.start();
47+
}
48+
49+
static class A {
50+
int i;
51+
}
52+
53+
54+
static A a1 = new A();
55+
static A a2 = new A();
56+
57+
58+
@Test
59+
@IR(counts = {IRNode.COUNTED_LOOP, "> 0"})
60+
static void testCountedLoop() {
61+
int zero = 34;
62+
63+
int limit = 2;
64+
for (; limit < 4; limit *= 2) ;
65+
for (int i = 2; i < limit; i++) {
66+
zero = 0;
67+
}
68+
69+
// Loop is not converted to a counted loop because a diamond is not removed due to missing to call
70+
// try_clean_mem_phi() again on the diamond region.
71+
int i = 0;
72+
do {
73+
iFld = 34;
74+
if (flag2) {
75+
iFld++;
76+
}
77+
78+
int z = 34;
79+
if (flag) {
80+
lateInline(); // Inlined late -> leaves a MergeMem
81+
if (zero == 34) { // False but only cleaned up after CCP
82+
iFld = 38;
83+
}
84+
z = 32; // Ensures to get a diamond If-Region
85+
}
86+
// Region merging a proper diamond after CCP with a memory phi merging loop phi and the MergeMem from lateInline().
87+
// Region is not added to the IGVN worklist anymore once the second phi dies.
88+
i++;
89+
} while (zero == 34 || (i < 1000 && a1 == a2)); // Could be converted to a counted loop after the diamond is removed after CCP.
90+
}
91+
92+
@Test
93+
@IR(failOn = IRNode.LOOP)
94+
static void testRemoveLoop() {
95+
int zero = 34;
96+
97+
int limit = 2;
98+
for (; limit < 4; limit *= 2) ;
99+
for (int i = 2; i < limit; i++) {
100+
zero = 0;
101+
}
102+
103+
// Loop is not converted to a counted loop and thus cannot be removed as empty loop because a diamond is not
104+
// removed due to missing to call try_clean_mem_phi() again on the diamond region.
105+
int i = 0;
106+
do {
107+
iFld = 34;
108+
109+
int z = 34;
110+
if (flag) {
111+
lateInline(); // Inlined late -> leaves a MergeMem
112+
if (zero == 34) { // False but only cleaned up after CCP
113+
iFld = 38;
114+
}
115+
z = 32; // Ensures to get a diamond If-Region
116+
}
117+
// Region merging a proper diamond after CCP with a memory phi merging loop phi and the MergeMem from lateInline().
118+
// Region is not added to the IGVN worklist anymore once the second phi dies.
119+
120+
i++;
121+
} while (zero == 34 || (i < 21 && a1 == a2));
122+
}
123+
124+
static void lateInline() {
125+
}
126+
}

0 commit comments

Comments
 (0)
Please sign in to comment.