Skip to content

Commit 10050a7

Browse files
committedSep 17, 2024
8332442: C2: refactor Mod cases in Compile::final_graph_reshaping_main_switch()
Reviewed-by: roland, chagedorn, jkarthikeyan
1 parent 7849f25 commit 10050a7

File tree

7 files changed

+284
-68
lines changed

7 files changed

+284
-68
lines changed
 

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

+28-68
Original file line numberDiff line numberDiff line change
@@ -3161,6 +3161,30 @@ void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Uni
31613161
}
31623162
}
31633163

3164+
void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) {
3165+
if (!UseDivMod) {
3166+
return;
3167+
}
3168+
3169+
// Check if "a % b" and "a / b" both exist
3170+
Node* d = n->find_similar(Op_DivIL(bt, is_unsigned));
3171+
if (d == nullptr) {
3172+
return;
3173+
}
3174+
3175+
// Replace them with a fused divmod if supported
3176+
if (Matcher::has_match_rule(Op_DivModIL(bt, is_unsigned))) {
3177+
DivModNode* divmod = DivModNode::make(n, bt, is_unsigned);
3178+
d->subsume_by(divmod->div_proj(), this);
3179+
n->subsume_by(divmod->mod_proj(), this);
3180+
} else {
3181+
// Replace "a % b" with "a - ((a / b) * b)"
3182+
Node* mult = MulNode::make(d, d->in(2), bt);
3183+
Node* sub = SubNode::make(d->in(1), mult, bt);
3184+
n->subsume_by(sub, this);
3185+
}
3186+
}
3187+
31643188
void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) {
31653189
switch( nop ) {
31663190
// Count all float operations that may use FPU
@@ -3609,83 +3633,19 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
36093633
#endif
36103634

36113635
case Op_ModI:
3612-
if (UseDivMod) {
3613-
// Check if a%b and a/b both exist
3614-
Node* d = n->find_similar(Op_DivI);
3615-
if (d) {
3616-
// Replace them with a fused divmod if supported
3617-
if (Matcher::has_match_rule(Op_DivModI)) {
3618-
DivModINode* divmod = DivModINode::make(n);
3619-
d->subsume_by(divmod->div_proj(), this);
3620-
n->subsume_by(divmod->mod_proj(), this);
3621-
} else {
3622-
// replace a%b with a-((a/b)*b)
3623-
Node* mult = new MulINode(d, d->in(2));
3624-
Node* sub = new SubINode(d->in(1), mult);
3625-
n->subsume_by(sub, this);
3626-
}
3627-
}
3628-
}
3636+
handle_div_mod_op(n, T_INT, false);
36293637
break;
36303638

36313639
case Op_ModL:
3632-
if (UseDivMod) {
3633-
// Check if a%b and a/b both exist
3634-
Node* d = n->find_similar(Op_DivL);
3635-
if (d) {
3636-
// Replace them with a fused divmod if supported
3637-
if (Matcher::has_match_rule(Op_DivModL)) {
3638-
DivModLNode* divmod = DivModLNode::make(n);
3639-
d->subsume_by(divmod->div_proj(), this);
3640-
n->subsume_by(divmod->mod_proj(), this);
3641-
} else {
3642-
// replace a%b with a-((a/b)*b)
3643-
Node* mult = new MulLNode(d, d->in(2));
3644-
Node* sub = new SubLNode(d->in(1), mult);
3645-
n->subsume_by(sub, this);
3646-
}
3647-
}
3648-
}
3640+
handle_div_mod_op(n, T_LONG, false);
36493641
break;
36503642

36513643
case Op_UModI:
3652-
if (UseDivMod) {
3653-
// Check if a%b and a/b both exist
3654-
Node* d = n->find_similar(Op_UDivI);
3655-
if (d) {
3656-
// Replace them with a fused unsigned divmod if supported
3657-
if (Matcher::has_match_rule(Op_UDivModI)) {
3658-
UDivModINode* divmod = UDivModINode::make(n);
3659-
d->subsume_by(divmod->div_proj(), this);
3660-
n->subsume_by(divmod->mod_proj(), this);
3661-
} else {
3662-
// replace a%b with a-((a/b)*b)
3663-
Node* mult = new MulINode(d, d->in(2));
3664-
Node* sub = new SubINode(d->in(1), mult);
3665-
n->subsume_by(sub, this);
3666-
}
3667-
}
3668-
}
3644+
handle_div_mod_op(n, T_INT, true);
36693645
break;
36703646

36713647
case Op_UModL:
3672-
if (UseDivMod) {
3673-
// Check if a%b and a/b both exist
3674-
Node* d = n->find_similar(Op_UDivL);
3675-
if (d) {
3676-
// Replace them with a fused unsigned divmod if supported
3677-
if (Matcher::has_match_rule(Op_UDivModL)) {
3678-
UDivModLNode* divmod = UDivModLNode::make(n);
3679-
d->subsume_by(divmod->div_proj(), this);
3680-
n->subsume_by(divmod->mod_proj(), this);
3681-
} else {
3682-
// replace a%b with a-((a/b)*b)
3683-
Node* mult = new MulLNode(d, d->in(2));
3684-
Node* sub = new SubLNode(d->in(1), mult);
3685-
n->subsume_by(sub, this);
3686-
}
3687-
}
3688-
}
3648+
handle_div_mod_op(n, T_LONG, true);
36893649
break;
36903650

36913651
case Op_LoadVector:

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

+1
Original file line numberDiff line numberDiff line change
@@ -1216,6 +1216,7 @@ class Compile : public Phase {
12161216
void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes);
12171217
void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes);
12181218
void eliminate_redundant_card_marks(Node* n);
1219+
void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned);
12191220

12201221
// Logic cone optimization.
12211222
void optimize_logic_cones(PhaseIterGVN &igvn);

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

+18
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,24 @@ DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3)
13601360
init_req(2, divisor);
13611361
}
13621362

1363+
DivModNode* DivModNode::make(Node* div_or_mod, BasicType bt, bool is_unsigned) {
1364+
assert(bt == T_INT || bt == T_LONG, "only int or long input pattern accepted");
1365+
1366+
if (bt == T_INT) {
1367+
if (is_unsigned) {
1368+
return UDivModINode::make(div_or_mod);
1369+
} else {
1370+
return DivModINode::make(div_or_mod);
1371+
}
1372+
} else {
1373+
if (is_unsigned) {
1374+
return UDivModLNode::make(div_or_mod);
1375+
} else {
1376+
return DivModLNode::make(div_or_mod);
1377+
}
1378+
}
1379+
}
1380+
13631381
//------------------------------make------------------------------------------
13641382
DivModINode* DivModINode::make(Node* div_or_mod) {
13651383
Node* n = div_or_mod;

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

+2
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,8 @@ class DivModNode : public MultiNode {
202202
virtual bool is_CFG() const { return false; }
203203
virtual uint ideal_reg() const { return NotAMachineReg; }
204204

205+
static DivModNode* make(Node* div_or_mod, BasicType bt, bool is_unsigned);
206+
205207
ProjNode* div_proj() { return proj_out_or_null(div_proj_num); }
206208
ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); }
207209
};

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

+32
Original file line numberDiff line numberDiff line change
@@ -2066,4 +2066,36 @@ inline int Op_Cast(BasicType bt) {
20662066
return Op_CastLL;
20672067
}
20682068

2069+
inline int Op_DivIL(BasicType bt, bool is_unsigned) {
2070+
assert(bt == T_INT || bt == T_LONG, "only for int or longs");
2071+
if (bt == T_INT) {
2072+
if (is_unsigned) {
2073+
return Op_UDivI;
2074+
} else {
2075+
return Op_DivI;
2076+
}
2077+
}
2078+
if (is_unsigned) {
2079+
return Op_UDivL;
2080+
} else {
2081+
return Op_DivL;
2082+
}
2083+
}
2084+
2085+
inline int Op_DivModIL(BasicType bt, bool is_unsigned) {
2086+
assert(bt == T_INT || bt == T_LONG, "only for int or longs");
2087+
if (bt == T_INT) {
2088+
if (is_unsigned) {
2089+
return Op_UDivModI;
2090+
} else {
2091+
return Op_DivModI;
2092+
}
2093+
}
2094+
if (is_unsigned) {
2095+
return Op_UDivModL;
2096+
} else {
2097+
return Op_DivModL;
2098+
}
2099+
}
2100+
20692101
#endif // SHARE_OPTO_NODE_HPP
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
/*
2+
* Copyright (c) 2024 Red Hat 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+
24+
package compiler.c2;
25+
26+
import compiler.lib.ir_framework.*;
27+
import compiler.lib.ir_framework.Test;
28+
29+
import java.util.Random;
30+
31+
/*
32+
* @test
33+
* @bug 8332442
34+
* @summary Test that DIV and MOD nodes are converted into DIVMOD where possible
35+
* @library /test/lib /
36+
* @run driver compiler.c2.TestDivModNodes
37+
*/
38+
public class TestDivModNodes {
39+
private static final Random RANDOM = AbstractInfo.getRandom();
40+
41+
private static int intQuotient;
42+
private static int intRemainder;
43+
private static long longQuotient;
44+
private static long longRemainder;
45+
46+
public static void main(String[] args) {
47+
TestFramework.runWithFlags("-XX:-UseDivMod");
48+
TestFramework.runWithFlags("-XX:+UseDivMod");
49+
}
50+
51+
private static int nextNonZeroInt() {
52+
int i;
53+
do {
54+
i = RANDOM.nextInt();
55+
} while (i == 0);
56+
return i;
57+
}
58+
59+
private static long nextNonZeroLong() {
60+
long i;
61+
do {
62+
i = RANDOM.nextLong();
63+
} while (i == 0);
64+
return i;
65+
}
66+
67+
@Test
68+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
69+
counts = {IRNode.DIV_MOD_I, "1"},
70+
failOn = {IRNode.DIV_I, IRNode.MOD_I})
71+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
72+
counts = {IRNode.DIV_I, "1", IRNode.MUL_I, "1", IRNode.SUB_I, "1"},
73+
failOn = {IRNode.MOD_I})
74+
@IR(applyIf = {"UseDivMod", "false"},
75+
counts = {IRNode.DIV_I, "1", IRNode.MOD_I, "1"})
76+
private static void testSignedIntDivMod(int dividend, int divisor) {
77+
intQuotient = dividend / divisor;
78+
intRemainder = dividend % divisor;
79+
}
80+
81+
@Run(test = "testSignedIntDivMod")
82+
private static void runSignedIntDivMod() {
83+
int dividend = RANDOM.nextInt();
84+
int divisor = nextNonZeroInt();
85+
testSignedIntDivMod(dividend, divisor);
86+
87+
verifyResult(dividend, divisor,
88+
intQuotient, intRemainder,
89+
dividend / divisor, dividend % divisor);
90+
}
91+
92+
93+
@Test
94+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
95+
counts = {IRNode.DIV_MOD_L, "1"},
96+
failOn = {IRNode.DIV_L, IRNode.MOD_L})
97+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
98+
counts = {IRNode.DIV_L, "1", IRNode.MUL_L, "1", IRNode.SUB_L, "1"},
99+
failOn = {IRNode.MOD_L})
100+
@IR(applyIf = {"UseDivMod", "false"},
101+
counts = {IRNode.DIV_L, "1", IRNode.MOD_L, "1"})
102+
private static void testSignedLongDivMod(long dividend, long divisor) {
103+
longQuotient = dividend / divisor;
104+
longRemainder = dividend % divisor;
105+
}
106+
107+
@Run(test = "testSignedLongDivMod")
108+
private static void runSignedLongDivMod() {
109+
long dividend = RANDOM.nextLong();
110+
long divisor = nextNonZeroLong();
111+
testSignedLongDivMod(dividend, divisor);
112+
113+
verifyResult(dividend, divisor,
114+
longQuotient, longRemainder,
115+
dividend / divisor, dividend % divisor);
116+
}
117+
118+
@Test
119+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
120+
counts = {IRNode.UDIV_MOD_I, "1"},
121+
failOn = {IRNode.UDIV_I, IRNode.UMOD_I})
122+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
123+
counts = {IRNode.UDIV_I, "1", IRNode.MUL_I, "1", IRNode.SUB_I, "1"},
124+
failOn = {IRNode.UMOD_I})
125+
@IR(applyIf = {"UseDivMod", "false"},
126+
counts = {IRNode.UDIV_I, "1", IRNode.UMOD_I, "1"})
127+
private static void testUnsignedIntDivMod(int dividend, int divisor) {
128+
intQuotient = Integer.divideUnsigned(dividend, divisor); // intrinsified on x86
129+
intRemainder = Integer.remainderUnsigned(dividend, divisor); // intrinsified on x86
130+
}
131+
132+
@Run(test = "testUnsignedIntDivMod")
133+
private static void runUnsignedIntDivMod() {
134+
int dividend = RANDOM.nextInt();
135+
int divisor = nextNonZeroInt();
136+
testUnsignedIntDivMod(dividend, divisor);
137+
138+
verifyResult(dividend, divisor,
139+
intQuotient, intRemainder,
140+
Integer.divideUnsigned(dividend, divisor), Integer.remainderUnsigned(dividend, divisor));
141+
}
142+
143+
@Test
144+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
145+
counts = {IRNode.UDIV_MOD_L, "1"},
146+
failOn = {IRNode.UDIV_L, IRNode.UMOD_L})
147+
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
148+
counts = {IRNode.UDIV_L, "1", IRNode.MUL_L, "1", IRNode.SUB_L, "1"},
149+
failOn = {IRNode.MOD_L})
150+
@IR(applyIf = {"UseDivMod", "false"},
151+
counts = {IRNode.UDIV_L, "1", IRNode.UMOD_L, "1"})
152+
private static void testUnsignedLongDivMod(long dividend, long divisor) {
153+
longQuotient = Long.divideUnsigned(dividend, divisor); // intrinsified on x86
154+
longRemainder = Long.remainderUnsigned(dividend, divisor); // intrinsified on x86
155+
}
156+
157+
@Run(test = "testUnsignedLongDivMod")
158+
private static void runUnsignedLongDivMod() {
159+
long dividend = RANDOM.nextLong();
160+
long divisor = nextNonZeroLong();
161+
testUnsignedLongDivMod(dividend, divisor);
162+
163+
verifyResult(dividend, divisor,
164+
longQuotient, longRemainder,
165+
Long.divideUnsigned(dividend, divisor), Long.remainderUnsigned(dividend, divisor));
166+
}
167+
168+
private static <T extends Number> void verifyResult(T dividend, T divisor,
169+
T quotient, T remainder,
170+
T expectedQ, T expectedR) {
171+
if (!expectedQ.equals(quotient) || !expectedR.equals(remainder)) {
172+
throw new AssertionError(String.format("Mismatched result from %d / %d. " +
173+
"Expected: quotient = %d remainder = %d, " +
174+
"but got: quotient = %d remainder = %d",
175+
dividend, divisor, expectedQ, expectedR, quotient, remainder));
176+
}
177+
}
178+
}

‎test/hotspot/jtreg/compiler/lib/ir_framework/IRNode.java

+25
Original file line numberDiff line numberDiff line change
@@ -514,11 +514,26 @@ public class IRNode {
514514
trapNodes(DIV_BY_ZERO_TRAP, "div0_check");
515515
}
516516

517+
public static final String DIV_I = PREFIX + "DIV_I" + POSTFIX;
518+
static {
519+
beforeMatchingNameRegex(DIV_I, "DivI");
520+
}
521+
517522
public static final String DIV_L = PREFIX + "DIV_L" + POSTFIX;
518523
static {
519524
beforeMatchingNameRegex(DIV_L, "DivL");
520525
}
521526

527+
public static final String DIV_MOD_I = PREFIX + "DIV_MOD_I" + POSTFIX;
528+
static {
529+
beforeMatchingNameRegex(DIV_MOD_I, "DivModI");
530+
}
531+
532+
public static final String DIV_MOD_L = PREFIX + "DIV_MOD_L" + POSTFIX;
533+
static {
534+
beforeMatchingNameRegex(DIV_MOD_L, "DivModL");
535+
}
536+
522537
public static final String DIV_VF = VECTOR_PREFIX + "DIV_VF" + POSTFIX;
523538
static {
524539
vectorNode(DIV_VF, "DivVF", TYPE_FLOAT);
@@ -997,6 +1012,16 @@ public class IRNode {
9971012
vectorNode(MIN_VL, "MinV", TYPE_LONG);
9981013
}
9991014

1015+
public static final String MOD_I = PREFIX + "MOD_I" + POSTFIX;
1016+
static {
1017+
beforeMatchingNameRegex(MOD_I, "ModI");
1018+
}
1019+
1020+
public static final String MOD_L = PREFIX + "MOD_L" + POSTFIX;
1021+
static {
1022+
beforeMatchingNameRegex(MOD_L, "ModL");
1023+
}
1024+
10001025
public static final String MUL = PREFIX + "MUL" + POSTFIX;
10011026
static {
10021027
beforeMatchingNameRegex(MUL, "Mul(I|L|F|D)");

0 commit comments

Comments
 (0)
Please sign in to comment.