mirror of
https://github.com/openjdk/jdk.git
synced 2026-04-14 00:49:42 +00:00
219 lines
7.6 KiB
Java
219 lines
7.6 KiB
Java
/*
|
|
* Copyright (c) 2026, IBM 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.intrinsics.math;
|
|
|
|
import jdk.test.lib.Asserts;
|
|
|
|
import compiler.lib.ir_framework.*;
|
|
import compiler.lib.generators.*;
|
|
import static compiler.lib.generators.Generators.*;
|
|
|
|
import java.util.Random;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8378713
|
|
* @key randomness
|
|
* @summary Math.pow(base, exp) should constant propagate
|
|
* @library /test/lib /
|
|
* @run driver ${test.main.class}
|
|
*/
|
|
public class PowDNodeTests {
|
|
public static final Generator<Double> UNIFORMS = G.uniformDoubles(); // [0, 1)
|
|
|
|
public static final double B = UNIFORMS.next() * 1000.0d;
|
|
public static final double E = UNIFORMS.next() * 1000.0d + 3.0d; // e >= 3 to avoid strength reduction code
|
|
|
|
public static void main(String[] args) {
|
|
TestFramework.run();
|
|
|
|
testCorrectness();
|
|
}
|
|
|
|
// Test 1: pow(2.0, 10.0) -> 1024.0
|
|
@Test
|
|
@IR(failOn = {IRNode.POW_D})
|
|
public static double constantLiteralFolding() {
|
|
return Math.pow(2.0, 10.0); // should fold to 1024.0
|
|
}
|
|
|
|
// Test 2: pow(final B, final E) -> B^E
|
|
@Test
|
|
@IR(failOn = {IRNode.POW_D})
|
|
public static double constantStaticFolding() {
|
|
return Math.pow(B, E); // should fold to B^E
|
|
}
|
|
|
|
// Test 3: pow(b, 0.0) -> 1.0
|
|
@Test
|
|
@IR(failOn = {IRNode.POW_D})
|
|
@Arguments(values = {Argument.RANDOM_EACH})
|
|
public static double expZero(double b) {
|
|
return Math.pow(b, 0.0);
|
|
}
|
|
|
|
// Test 4: pow(b, 1.0) -> b (identity)
|
|
@Test
|
|
@IR(failOn = {IRNode.POW_D})
|
|
@Arguments(values = {Argument.RANDOM_EACH})
|
|
public static double expOne(double b) {
|
|
return Math.pow(b, 1.0);
|
|
}
|
|
|
|
// Test 5: pow(b, NaN) -> NaN
|
|
@Test
|
|
@IR(failOn = {IRNode.POW_D})
|
|
@Arguments(values = {Argument.RANDOM_EACH})
|
|
public static double expNaN(double b) {
|
|
return Math.pow(b, Double.NaN);
|
|
}
|
|
|
|
// Test 6: pow(b, 2.0) -> b * b
|
|
// More tests in TestPow2Opt.java
|
|
@Test
|
|
@IR(failOn = {IRNode.POW_D})
|
|
@IR(counts = {IRNode.MUL_D, "1"})
|
|
@Arguments(values = {Argument.RANDOM_EACH})
|
|
public static double expTwo(double b) {
|
|
return Math.pow(b, 2.0);
|
|
}
|
|
|
|
// Test 7: pow(b, 0.5) -> b <= 0.0 ? pow(b, 0.5) : sqrt(b)
|
|
// More tests in TestPow0Dot5Opt.java
|
|
@Test
|
|
@IR(counts = {IRNode.IF, "1"})
|
|
@IR(counts = {IRNode.SQRT_D, "1"})
|
|
@IR(counts = {".*CallLeaf.*pow.*", "1"}, phase = CompilePhase.BEFORE_MATCHING)
|
|
@Arguments(values = {Argument.RANDOM_EACH})
|
|
public static double expDot5(double b) {
|
|
return Math.pow(b, 0.5); // expand to: if (b > 0) { sqrt(b) } else { call(b) }
|
|
}
|
|
|
|
// Test 8: non-constant exponent stays as call
|
|
@Test
|
|
@IR(counts = {IRNode.POW_D, "1"})
|
|
@Arguments(values = {Argument.RANDOM_EACH, Argument.RANDOM_EACH})
|
|
public static double nonConstant(double b, double e) {
|
|
return Math.pow(b, e);
|
|
}
|
|
|
|
// Test 9: late constant discovery on base (after loop opts)
|
|
@Test
|
|
@IR(counts = {IRNode.POW_D, "1"}, phase = CompilePhase.AFTER_PARSING)
|
|
@IR(failOn = {IRNode.POW_D})
|
|
public static double lateBaseConstant() {
|
|
double base = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
if ((i % 2) == 0) {
|
|
base = B;
|
|
}
|
|
}
|
|
// After loop opts, base == B (constant), so pow(B, E) folds
|
|
return Math.pow(base, E);
|
|
}
|
|
|
|
// Test 10: late constant discovery on exp (after loop opts)
|
|
@Test
|
|
@IR(counts = {IRNode.POW_D, "1"}, phase = CompilePhase.AFTER_PARSING)
|
|
@IR(failOn = {IRNode.POW_D})
|
|
public static double lateExpConstant() {
|
|
double exp = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
if ((i % 2) == 0) {
|
|
exp = E;
|
|
}
|
|
}
|
|
// After loop opts, exp == E (constant), so pow(B, E) folds
|
|
return Math.pow(B, exp);
|
|
}
|
|
|
|
// Test 11: late constant discoveries on both base and exp (after loop opts)
|
|
@Test
|
|
@IR(counts = {IRNode.POW_D, "1"}, phase = CompilePhase.AFTER_PARSING)
|
|
@IR(failOn = {IRNode.POW_D})
|
|
public static double lateBothConstant() {
|
|
double base = 0, exp = 0;
|
|
for (int i = 0; i < 4; i++) {
|
|
if ((i % 2) == 0) {
|
|
base = B;
|
|
exp = E;
|
|
}
|
|
}
|
|
// After loop opts, base = B, exp == E, so pow(B, E) folds
|
|
return Math.pow(base, exp);
|
|
}
|
|
|
|
private static void assertEQWithinOneUlp(double expected, double observed) {
|
|
if (Double.isNaN(expected) && Double.isNaN(observed)) return;
|
|
|
|
// Math.pow() requires result must be within 1 ulp of the respective magnitude
|
|
double ulp = Math.max(Math.ulp(expected), Math.ulp(observed));
|
|
if (Math.abs(expected - observed) > ulp) {
|
|
throw new AssertionError(String.format(
|
|
"expect = %x, observed = %x, ulp = %x",
|
|
Double.doubleToRawLongBits(expected), Double.doubleToRawLongBits(observed), Double.doubleToRawLongBits(ulp)
|
|
));
|
|
}
|
|
}
|
|
|
|
private static void testCorrectness() {
|
|
// No need to warm up for intrinsics
|
|
Asserts.assertEQ(1024.0d, constantLiteralFolding());
|
|
|
|
double BE = StrictMath.pow(B, E);
|
|
assertEQWithinOneUlp(BE, constantStaticFolding());
|
|
assertEQWithinOneUlp(BE, lateBaseConstant());
|
|
assertEQWithinOneUlp(BE, lateExpConstant());
|
|
assertEQWithinOneUlp(BE, lateBothConstant());
|
|
|
|
Generator<Double> anyBits = G.anyBitsDouble();
|
|
Generator<Double> largeDoubles = G.uniformDoubles(Long.MAX_VALUE, Double.MAX_VALUE);
|
|
Generator<Double> doubles = G.doubles();
|
|
double[] values = {
|
|
Double.MIN_VALUE, Double.MIN_NORMAL, -42.0d, -1.0d, -0.0d, +0.0d, 0.5d, 1.0d, 2.0d, 123d, Double.MAX_VALUE,
|
|
Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, Double.NaN,
|
|
UNIFORMS.next(), UNIFORMS.next(),
|
|
largeDoubles.next(), -largeDoubles.next(), // some sufficiently large magnitudes
|
|
anyBits.next(), anyBits.next(), // any bits with potentially more NaN representation
|
|
doubles.next(), doubles.next() // a healthy sprinkle of whatever else is possible
|
|
};
|
|
|
|
for (double b : values) {
|
|
// Strength reduced, so we know the bits matches exactly
|
|
Asserts.assertEQ(1.0d, expZero(b));
|
|
Asserts.assertEQ(b, expOne(b));
|
|
Asserts.assertEQ(b * b, expTwo(b));
|
|
|
|
assertEQWithinOneUlp(Double.NaN, expNaN(b));
|
|
|
|
// Runtime calls, so make sure the result is within 1 ulp
|
|
assertEQWithinOneUlp(StrictMath.pow(b, 0.5d), expDot5(b));
|
|
|
|
for (double e : values) {
|
|
assertEQWithinOneUlp(StrictMath.pow(b, e), nonConstant(b, e));
|
|
}
|
|
}
|
|
}
|
|
}
|