Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 28 additions & 68 deletions src/hotspot/share/opto/compile.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3161,6 +3161,30 @@ void Compile::final_graph_reshaping_impl(Node *n, Final_Reshape_Counts& frc, Uni
}
}

void Compile::handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned) {
if (!UseDivMod) {
return;
}

// Check if "a % b" and "a / b" both exist
Node* d = n->find_similar(Op_DivIL(bt, is_unsigned));
if (d == nullptr) {
return;
}

// Replace them with a fused divmod if supported
if (Matcher::has_match_rule(Op_DivModIL(bt, is_unsigned))) {
DivModNode* divmod = DivModNode::make(n, bt, is_unsigned);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// Replace "a % b" with "a - ((a / b) * b)"
Node* mult = MulNode::make(d, d->in(2), bt);
Node* sub = SubNode::make(d->in(1), mult, bt);
n->subsume_by(sub, this);
}
}

void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes) {
switch( nop ) {
// Count all float operations that may use FPU
Expand Down Expand Up @@ -3609,83 +3633,19 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
#endif

case Op_ModI:
if (UseDivMod) {
// Check if a%b and a/b both exist
Node* d = n->find_similar(Op_DivI);
if (d) {
// Replace them with a fused divmod if supported
if (Matcher::has_match_rule(Op_DivModI)) {
DivModINode* divmod = DivModINode::make(n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new MulINode(d, d->in(2));
Node* sub = new SubINode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
handle_div_mod_op(n, T_INT, false);
break;

case Op_ModL:
if (UseDivMod) {
// Check if a%b and a/b both exist
Node* d = n->find_similar(Op_DivL);
if (d) {
// Replace them with a fused divmod if supported
if (Matcher::has_match_rule(Op_DivModL)) {
DivModLNode* divmod = DivModLNode::make(n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new MulLNode(d, d->in(2));
Node* sub = new SubLNode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
handle_div_mod_op(n, T_LONG, false);
break;

case Op_UModI:
if (UseDivMod) {
// Check if a%b and a/b both exist
Node* d = n->find_similar(Op_UDivI);
if (d) {
// Replace them with a fused unsigned divmod if supported
if (Matcher::has_match_rule(Op_UDivModI)) {
UDivModINode* divmod = UDivModINode::make(n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new MulINode(d, d->in(2));
Node* sub = new SubINode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
handle_div_mod_op(n, T_INT, true);
break;

case Op_UModL:
if (UseDivMod) {
// Check if a%b and a/b both exist
Node* d = n->find_similar(Op_UDivL);
if (d) {
// Replace them with a fused unsigned divmod if supported
if (Matcher::has_match_rule(Op_UDivModL)) {
UDivModLNode* divmod = UDivModLNode::make(n);
d->subsume_by(divmod->div_proj(), this);
n->subsume_by(divmod->mod_proj(), this);
} else {
// replace a%b with a-((a/b)*b)
Node* mult = new MulLNode(d, d->in(2));
Node* sub = new SubLNode(d->in(1), mult);
n->subsume_by(sub, this);
}
}
}
handle_div_mod_op(n, T_LONG, true);
break;

case Op_LoadVector:
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/opto/compile.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1216,6 +1216,7 @@ class Compile : public Phase {
void final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& frc, uint nop, Unique_Node_List& dead_nodes);
void final_graph_reshaping_walk(Node_Stack& nstack, Node* root, Final_Reshape_Counts& frc, Unique_Node_List& dead_nodes);
void eliminate_redundant_card_marks(Node* n);
void handle_div_mod_op(Node* n, BasicType bt, bool is_unsigned);

// Logic cone optimization.
void optimize_logic_cones(PhaseIterGVN &igvn);
Expand Down
18 changes: 18 additions & 0 deletions src/hotspot/share/opto/divnode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1360,6 +1360,24 @@ DivModNode::DivModNode( Node *c, Node *dividend, Node *divisor ) : MultiNode(3)
init_req(2, divisor);
}

DivModNode* DivModNode::make(Node* div_or_mod, BasicType bt, bool is_unsigned) {
assert(bt == T_INT || bt == T_LONG, "only int or long input pattern accepted");

if (bt == T_INT) {
if (is_unsigned) {
return UDivModINode::make(div_or_mod);
} else {
return DivModINode::make(div_or_mod);
}
} else {
if (is_unsigned) {
return UDivModLNode::make(div_or_mod);
} else {
return DivModLNode::make(div_or_mod);
}
}
}

//------------------------------make------------------------------------------
DivModINode* DivModINode::make(Node* div_or_mod) {
Node* n = div_or_mod;
Expand Down
2 changes: 2 additions & 0 deletions src/hotspot/share/opto/divnode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ class DivModNode : public MultiNode {
virtual bool is_CFG() const { return false; }
virtual uint ideal_reg() const { return NotAMachineReg; }

static DivModNode* make(Node* div_or_mod, BasicType bt, bool is_unsigned);

ProjNode* div_proj() { return proj_out_or_null(div_proj_num); }
ProjNode* mod_proj() { return proj_out_or_null(mod_proj_num); }
};
Expand Down
32 changes: 32 additions & 0 deletions src/hotspot/share/opto/node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2066,4 +2066,36 @@ inline int Op_Cast(BasicType bt) {
return Op_CastLL;
}

inline int Op_DivIL(BasicType bt, bool is_unsigned) {
assert(bt == T_INT || bt == T_LONG, "only for int or longs");
if (bt == T_INT) {
if (is_unsigned) {
return Op_UDivI;
} else {
return Op_DivI;
}
}
if (is_unsigned) {
return Op_UDivL;
} else {
return Op_DivL;
}
}

inline int Op_DivModIL(BasicType bt, bool is_unsigned) {
assert(bt == T_INT || bt == T_LONG, "only for int or longs");
if (bt == T_INT) {
if (is_unsigned) {
return Op_UDivModI;
} else {
return Op_DivModI;
}
}
if (is_unsigned) {
return Op_UDivModL;
} else {
return Op_DivModL;
}
}

#endif // SHARE_OPTO_NODE_HPP
178 changes: 178 additions & 0 deletions test/hotspot/jtreg/compiler/c2/TestDivModNodes.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
/*
* Copyright (c) 2024 Red Hat and/or its affiliates. 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;

import compiler.lib.ir_framework.*;
import compiler.lib.ir_framework.Test;

import java.util.Random;

/*
* @test
* @bug 8332442
* @summary Test that DIV and MOD nodes are converted into DIVMOD where possible
* @library /test/lib /
* @run driver compiler.c2.TestDivModNodes
*/
public class TestDivModNodes {
private static final Random RANDOM = AbstractInfo.getRandom();

private static int intQuotient;
private static int intRemainder;
private static long longQuotient;
private static long longRemainder;

public static void main(String[] args) {
TestFramework.runWithFlags("-XX:-UseDivMod");
TestFramework.runWithFlags("-XX:+UseDivMod");
}

private static int nextNonZeroInt() {
int i;
do {
i = RANDOM.nextInt();
} while (i == 0);
return i;
}

private static long nextNonZeroLong() {
long i;
do {
i = RANDOM.nextLong();
} while (i == 0);
return i;
}

@Test
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
counts = {IRNode.DIV_MOD_I, "1"},
failOn = {IRNode.DIV_I, IRNode.MOD_I})
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
counts = {IRNode.DIV_I, "1", IRNode.MUL_I, "1", IRNode.SUB_I, "1"},
failOn = {IRNode.MOD_I})
@IR(applyIf = {"UseDivMod", "false"},
counts = {IRNode.DIV_I, "1", IRNode.MOD_I, "1"})
private static void testSignedIntDivMod(int dividend, int divisor) {
intQuotient = dividend / divisor;
intRemainder = dividend % divisor;
}

@Run(test = "testSignedIntDivMod")
private static void runSignedIntDivMod() {
int dividend = RANDOM.nextInt();
int divisor = nextNonZeroInt();
testSignedIntDivMod(dividend, divisor);

verifyResult(dividend, divisor,
intQuotient, intRemainder,
dividend / divisor, dividend % divisor);
}


@Test
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
counts = {IRNode.DIV_MOD_L, "1"},
failOn = {IRNode.DIV_L, IRNode.MOD_L})
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
counts = {IRNode.DIV_L, "1", IRNode.MUL_L, "1", IRNode.SUB_L, "1"},
failOn = {IRNode.MOD_L})
@IR(applyIf = {"UseDivMod", "false"},
counts = {IRNode.DIV_L, "1", IRNode.MOD_L, "1"})
private static void testSignedLongDivMod(long dividend, long divisor) {
longQuotient = dividend / divisor;
longRemainder = dividend % divisor;
}

@Run(test = "testSignedLongDivMod")
private static void runSignedLongDivMod() {
long dividend = RANDOM.nextLong();
long divisor = nextNonZeroLong();
testSignedLongDivMod(dividend, divisor);

verifyResult(dividend, divisor,
longQuotient, longRemainder,
dividend / divisor, dividend % divisor);
}

@Test
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
counts = {IRNode.UDIV_MOD_I, "1"},
failOn = {IRNode.UDIV_I, IRNode.UMOD_I})
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
counts = {IRNode.UDIV_I, "1", IRNode.MUL_I, "1", IRNode.SUB_I, "1"},
failOn = {IRNode.UMOD_I})
@IR(applyIf = {"UseDivMod", "false"},
counts = {IRNode.UDIV_I, "1", IRNode.UMOD_I, "1"})
private static void testUnsignedIntDivMod(int dividend, int divisor) {
intQuotient = Integer.divideUnsigned(dividend, divisor); // intrinsified on x86
intRemainder = Integer.remainderUnsigned(dividend, divisor); // intrinsified on x86
}

@Run(test = "testUnsignedIntDivMod")
private static void runUnsignedIntDivMod() {
int dividend = RANDOM.nextInt();
int divisor = nextNonZeroInt();
testUnsignedIntDivMod(dividend, divisor);

verifyResult(dividend, divisor,
intQuotient, intRemainder,
Integer.divideUnsigned(dividend, divisor), Integer.remainderUnsigned(dividend, divisor));
}

@Test
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"x64", "true"},
counts = {IRNode.UDIV_MOD_L, "1"},
failOn = {IRNode.UDIV_L, IRNode.UMOD_L})
@IR(applyIf = {"UseDivMod", "true"}, applyIfPlatform = {"aarch64", "true"},
counts = {IRNode.UDIV_L, "1", IRNode.MUL_L, "1", IRNode.SUB_L, "1"},
failOn = {IRNode.MOD_L})
@IR(applyIf = {"UseDivMod", "false"},
counts = {IRNode.UDIV_L, "1", IRNode.UMOD_L, "1"})
private static void testUnsignedLongDivMod(long dividend, long divisor) {
longQuotient = Long.divideUnsigned(dividend, divisor); // intrinsified on x86
longRemainder = Long.remainderUnsigned(dividend, divisor); // intrinsified on x86
}

@Run(test = "testUnsignedLongDivMod")
private static void runUnsignedLongDivMod() {
long dividend = RANDOM.nextLong();
long divisor = nextNonZeroLong();
testUnsignedLongDivMod(dividend, divisor);

verifyResult(dividend, divisor,
longQuotient, longRemainder,
Long.divideUnsigned(dividend, divisor), Long.remainderUnsigned(dividend, divisor));
}

private static <T extends Number> void verifyResult(T dividend, T divisor,
T quotient, T remainder,
T expectedQ, T expectedR) {
if (!expectedQ.equals(quotient) || !expectedR.equals(remainder)) {
throw new AssertionError(String.format("Mismatched result from %d / %d. " +
"Expected: quotient = %d remainder = %d, " +
"but got: quotient = %d remainder = %d",
dividend, divisor, expectedQ, expectedR, quotient, remainder));
}
}
}
Loading