Skip to content

Commit 10afb04

Browse files
committed
[GR-65747] Canonicalize F2D->RoundD->D2F chain to RoundF.
PullRequest: graal/21061
2 parents 3e5017c + 850133f commit 10afb04

File tree

4 files changed

+151
-10
lines changed

4 files changed

+151
-10
lines changed
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Copyright (c) 2025, 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. Oracle designates this
8+
* particular file as subject to the "Classpath" exception as provided
9+
* by Oracle in the LICENSE file that accompanied this code.
10+
*
11+
* This code is distributed in the hope that it will be useful, but WITHOUT
12+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+
* version 2 for more details (a copy is included in the LICENSE file that
15+
* accompanied this code).
16+
*
17+
* You should have received a copy of the GNU General Public License version
18+
* 2 along with this work; if not, write to the Free Software Foundation,
19+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+
*
21+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+
* or visit www.oracle.com if you need additional information or have any
23+
* questions.
24+
*/
25+
package jdk.graal.compiler.jtt.lang;
26+
27+
import java.util.ArrayList;
28+
import java.util.Collection;
29+
30+
import org.junit.Test;
31+
import org.junit.runner.RunWith;
32+
import org.junit.runners.Parameterized;
33+
import org.junit.runners.Parameterized.Parameter;
34+
import org.junit.runners.Parameterized.Parameters;
35+
36+
import jdk.graal.compiler.jtt.JTTTest;
37+
38+
/**
39+
* Tests {@link Math#floor}, {@link Math#ceil}, {@link Math#rint} with {@code float} arguments.
40+
*
41+
* @see Math_round
42+
*/
43+
@RunWith(Parameterized.class)
44+
public class Math_round_f32 extends JTTTest {
45+
46+
@Parameter(value = 0) public float input;
47+
48+
public static float rintFF(float arg) {
49+
return (float) Math.rint(arg);
50+
}
51+
52+
public static double rintFD(float arg) {
53+
return Math.rint(arg);
54+
}
55+
56+
public static float floorFF(float arg) {
57+
return (float) Math.floor(arg);
58+
}
59+
60+
public static double floorFD(float arg) {
61+
return Math.floor(arg);
62+
}
63+
64+
public static float ceilFF(float arg) {
65+
return (float) Math.ceil(arg);
66+
}
67+
68+
public static double ceilFD(float arg) {
69+
return Math.ceil(arg);
70+
}
71+
72+
@Test
73+
public void runRint() throws Throwable {
74+
runTest("rintFF", input);
75+
runTest("rintFD", input);
76+
}
77+
78+
@Test
79+
public void runFloor() throws Throwable {
80+
runTest("floorFF", input);
81+
runTest("floorFD", input);
82+
}
83+
84+
@Test
85+
public void runCeil() throws Throwable {
86+
runTest("ceilFF", input);
87+
runTest("ceilFD", input);
88+
}
89+
90+
@Parameters(name = "{0}")
91+
public static Collection<Object[]> data() {
92+
ArrayList<Object[]> tests = new ArrayList<>();
93+
for (int i = -3; i < 3; i++) {
94+
addTest(tests, i);
95+
addTest(tests, i + 0.2f);
96+
addTest(tests, i + 0.5f);
97+
addTest(tests, i + 0.7f);
98+
}
99+
addTest(tests, -0.0f);
100+
for (float sign : new float[]{-1f, 1f}) {
101+
// largest non-integral value representable in float
102+
float largestNonIntegralValue = Float.intBitsToFloat(0b0_10010101_1111111_11111111_11111111);
103+
assert largestNonIntegralValue == 8388607.5f : largestNonIntegralValue;
104+
addTest(tests, Math.copySign(largestNonIntegralValue, sign));
105+
addTest(tests, Math.copySign(Math.nextDown(largestNonIntegralValue), sign));
106+
addTest(tests, Math.copySign(Math.nextUp(largestNonIntegralValue), sign));
107+
108+
addTest(tests, Math.copySign(Float.MIN_VALUE, sign));
109+
addTest(tests, Math.copySign(Float.MAX_VALUE, sign));
110+
}
111+
addTest(tests, Float.NEGATIVE_INFINITY);
112+
addTest(tests, Float.POSITIVE_INFINITY);
113+
addTest(tests, Float.NaN);
114+
addTest(tests, Float.intBitsToFloat(0xffc00000)); // negative quiet NaN
115+
addTest(tests, Float.intBitsToFloat(0x7f800001)); // positive signaling NaN
116+
addTest(tests, Float.intBitsToFloat(0xffbfffff)); // negative signaling NaN
117+
return tests;
118+
}
119+
120+
private static void addTest(ArrayList<Object[]> tests, float input) {
121+
tests.add(new Object[]{input});
122+
}
123+
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/calc/RoundNode.java

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2016, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -27,21 +27,21 @@
2727

2828
import static jdk.graal.compiler.nodeinfo.NodeCycles.CYCLES_1;
2929

30+
import jdk.graal.compiler.core.common.calc.FloatConvert;
3031
import jdk.graal.compiler.core.common.type.FloatStamp;
3132
import jdk.graal.compiler.core.common.type.Stamp;
3233
import jdk.graal.compiler.debug.GraalError;
3334
import jdk.graal.compiler.graph.NodeClass;
35+
import jdk.graal.compiler.lir.gen.ArithmeticLIRGeneratorTool;
36+
import jdk.graal.compiler.lir.gen.ArithmeticLIRGeneratorTool.RoundingMode;
37+
import jdk.graal.compiler.nodeinfo.NodeInfo;
3438
import jdk.graal.compiler.nodes.ConstantNode;
3539
import jdk.graal.compiler.nodes.NodeView;
3640
import jdk.graal.compiler.nodes.ValueNode;
3741
import jdk.graal.compiler.nodes.spi.ArithmeticLIRLowerable;
3842
import jdk.graal.compiler.nodes.spi.CanonicalizerTool;
3943
import jdk.graal.compiler.nodes.spi.LoweringProvider;
4044
import jdk.graal.compiler.nodes.spi.NodeLIRBuilderTool;
41-
import jdk.graal.compiler.lir.gen.ArithmeticLIRGeneratorTool;
42-
import jdk.graal.compiler.lir.gen.ArithmeticLIRGeneratorTool.RoundingMode;
43-
import jdk.graal.compiler.nodeinfo.NodeInfo;
44-
4545
import jdk.vm.ci.meta.JavaConstant;
4646
import jdk.vm.ci.meta.JavaKind;
4747

@@ -63,6 +63,14 @@ public RoundNode(ValueNode value, RoundingMode mode) {
6363
this.mode = mode;
6464
}
6565

66+
public static ValueNode create(ValueNode input, RoundingMode mode) {
67+
ValueNode folded = tryFold(input, mode);
68+
if (folded != null) {
69+
return folded;
70+
}
71+
return new RoundNode(input, mode);
72+
}
73+
6674
public RoundingMode mode() {
6775
return mode;
6876
}
@@ -102,7 +110,7 @@ public Stamp foldStamp(Stamp newStamp) {
102110
return roundStamp((FloatStamp) newStamp, mode);
103111
}
104112

105-
public ValueNode tryFold(ValueNode input) {
113+
public static ValueNode tryFold(ValueNode input, RoundingMode mode) {
106114
if (input.isConstant()) {
107115
JavaConstant c = input.asJavaConstant();
108116
if (c.getJavaKind() == JavaKind.Double) {
@@ -116,8 +124,18 @@ public ValueNode tryFold(ValueNode input) {
116124

117125
@Override
118126
public ValueNode canonical(CanonicalizerTool tool, ValueNode forValue) {
119-
ValueNode folded = tryFold(forValue);
120-
return folded != null ? folded : this;
127+
ValueNode folded = tryFold(forValue, mode);
128+
if (folded != null) {
129+
return folded;
130+
}
131+
/*
132+
* Try to replace F2D->RoundD->D2F with RoundF. First replace F2D->RoundD with RoundF->F2D,
133+
* then rely on further FloatConvertNode canonicalization to eliminate F2D->D2F, if any.
134+
*/
135+
if (forValue instanceof FloatConvertNode convert && convert.getFloatConvert() == FloatConvert.F2D) {
136+
return FloatConvertNode.create(FloatConvert.F2D, RoundNode.create(convert.getValue(), mode), NodeView.from(tool));
137+
}
138+
return this;
121139
}
122140

123141
@Override

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/replacements/StandardGraphBuilderPlugins.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1207,7 +1207,7 @@ private static void registerRound(boolean supportsRound, Registration r, String
12071207
r.registerConditional(supportsRound, new InvocationPlugin(name, double.class) {
12081208
@Override
12091209
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode arg) {
1210-
b.push(JavaKind.Double, b.append(new RoundNode(arg, mode)));
1210+
b.push(JavaKind.Double, b.append(RoundNode.create(arg, mode)));
12111211
return true;
12121212
}
12131213
});

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/truffle/substitutions/TruffleGraphBuilderPlugins.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,7 @@ public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Rec
291291

292292
@Override
293293
public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode x) {
294-
b.addPush(kind, new RoundNode(x, RoundingMode.TRUNCATE));
294+
b.addPush(kind, RoundNode.create(x, RoundingMode.TRUNCATE));
295295
return true;
296296
}
297297

0 commit comments

Comments
 (0)