Skip to content

Commit 9e70aac

Browse files
committed
Add unsigned integer from/to float conversion ops.
1 parent 2c66b55 commit 9e70aac

File tree

8 files changed

+303
-39
lines changed

8 files changed

+303
-39
lines changed

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/NumUtil.java

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 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
@@ -442,4 +442,39 @@ public static int unsafeAbs(int i) {
442442
public static long unsafeAbs(long l) {
443443
return Math.abs(l);
444444
}
445+
446+
/**
447+
* Converts the double value to unsigned long, rounding toward zero.
448+
*/
449+
public static long toUnsignedLong(double value) {
450+
if (value >= 0x1p63) {
451+
return (long) (value - 0x1p63) + Long.MIN_VALUE;
452+
} else if (value >= 0) {
453+
return (long) value;
454+
} else {
455+
return 0;
456+
}
457+
}
458+
459+
/**
460+
* Converts the unsigned long value to the nearest double value.
461+
*/
462+
public static double unsignedToDouble(long value) {
463+
if (value >= 0) {
464+
return value;
465+
} else {
466+
return ((value >>> 1) | (value & 1)) * 2.0;
467+
}
468+
}
469+
470+
/**
471+
* Converts the unsigned long value to the nearest float value.
472+
*/
473+
public static float unsignedToFloat(long value) {
474+
if (value >= 0) {
475+
return value;
476+
} else {
477+
return ((value >>> 1) | (value & 1)) * 2.0f;
478+
}
479+
}
445480
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/calc/FloatConvert.java

Lines changed: 33 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2014, 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
@@ -40,7 +40,17 @@ public enum FloatConvert {
4040
D2F(FloatConvertCategory.FloatingPointToFloatingPoint, 64),
4141
I2D(FloatConvertCategory.IntegerToFloatingPoint, 32),
4242
L2D(FloatConvertCategory.IntegerToFloatingPoint, 64),
43-
F2D(FloatConvertCategory.FloatingPointToFloatingPoint, 32);
43+
F2D(FloatConvertCategory.FloatingPointToFloatingPoint, 32),
44+
45+
// to or from unsigned integer
46+
F2UI(FloatConvertCategory.FloatingPointToInteger, 32),
47+
D2UI(FloatConvertCategory.FloatingPointToInteger, 64),
48+
F2UL(FloatConvertCategory.FloatingPointToInteger, 32),
49+
D2UL(FloatConvertCategory.FloatingPointToInteger, 64),
50+
UI2F(FloatConvertCategory.IntegerToFloatingPoint, 32),
51+
UL2F(FloatConvertCategory.IntegerToFloatingPoint, 64),
52+
UI2D(FloatConvertCategory.IntegerToFloatingPoint, 32),
53+
UL2D(FloatConvertCategory.IntegerToFloatingPoint, 64);
4454

4555
private final FloatConvertCategory category;
4656
private final int inputBits;
@@ -55,30 +65,27 @@ public FloatConvertCategory getCategory() {
5565
}
5666

5767
public FloatConvert reverse() {
58-
switch (this) {
59-
case D2F:
60-
return F2D;
61-
case D2I:
62-
return I2D;
63-
case D2L:
64-
return L2D;
65-
case F2D:
66-
return D2F;
67-
case F2I:
68-
return I2F;
69-
case F2L:
70-
return L2F;
71-
case I2D:
72-
return D2I;
73-
case I2F:
74-
return F2I;
75-
case L2D:
76-
return D2L;
77-
case L2F:
78-
return F2L;
79-
default:
80-
throw GraalError.shouldNotReachHereUnexpectedValue(this); // ExcludeFromJacocoGeneratedReport
81-
}
68+
return switch (this) {
69+
case D2F -> F2D;
70+
case D2I -> I2D;
71+
case D2L -> L2D;
72+
case F2D -> D2F;
73+
case F2I -> I2F;
74+
case F2L -> L2F;
75+
case I2D -> D2I;
76+
case I2F -> F2I;
77+
case L2D -> D2L;
78+
case L2F -> F2L;
79+
case F2UI -> UI2F;
80+
case D2UI -> UI2D;
81+
case F2UL -> UL2F;
82+
case D2UL -> UL2D;
83+
case UI2F -> F2UI;
84+
case UL2F -> F2UL;
85+
case UI2D -> D2UI;
86+
case UL2D -> D2UL;
87+
default -> throw GraalError.shouldNotReachHereUnexpectedValue(this); // ExcludeFromJacocoGeneratedReport
88+
};
8289
}
8390

8491
public int getInputBits() {

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/common/type/FloatStamp.java

Lines changed: 130 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2012, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2012, 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,9 +27,13 @@
2727
import static jdk.graal.compiler.core.common.calc.FloatConvert.D2F;
2828
import static jdk.graal.compiler.core.common.calc.FloatConvert.D2I;
2929
import static jdk.graal.compiler.core.common.calc.FloatConvert.D2L;
30+
import static jdk.graal.compiler.core.common.calc.FloatConvert.D2UI;
31+
import static jdk.graal.compiler.core.common.calc.FloatConvert.D2UL;
3032
import static jdk.graal.compiler.core.common.calc.FloatConvert.F2D;
3133
import static jdk.graal.compiler.core.common.calc.FloatConvert.F2I;
3234
import static jdk.graal.compiler.core.common.calc.FloatConvert.F2L;
35+
import static jdk.graal.compiler.core.common.calc.FloatConvert.F2UI;
36+
import static jdk.graal.compiler.core.common.calc.FloatConvert.F2UL;
3337

3438
import java.nio.ByteBuffer;
3539
import java.util.function.DoubleBinaryOperator;
@@ -456,21 +460,27 @@ private static Stamp maybeFoldConstant(ArithmeticOpTable.BinaryOp<?> op, FloatSt
456460
}
457461

458462
public static boolean floatingToIntegerCanOverflow(FloatStamp floatStamp, int integerBits) {
463+
return floatingToIntegerCanOverflow(floatStamp, integerBits, false);
464+
}
465+
466+
public static boolean floatingToIntegerCanOverflow(FloatStamp floatStamp, int integerBits, boolean unsigned) {
459467
final boolean canBeInfinity = Double.isInfinite(floatStamp.lowerBound()) || Double.isInfinite(floatStamp.upperBound());
460468
if (canBeInfinity) {
461469
return true;
462470
}
463-
final boolean conversionCanOverflow = integralPartLargerMaxValue(floatStamp, integerBits) || integralPartSmallerMinValue(floatStamp, integerBits);
471+
final boolean conversionCanOverflow = integralPartLargerMaxValue(floatStamp, integerBits, unsigned) || integralPartSmallerMinValue(floatStamp, integerBits, unsigned);
464472
return conversionCanOverflow;
465473
}
466474

467-
private static boolean integralPartLargerMaxValue(FloatStamp floatStamp, int integerBits) {
475+
private static boolean integralPartLargerMaxValue(FloatStamp floatStamp, int integerBits, boolean unsigned) {
468476
double upperBound = floatStamp.upperBound();
469477
if (Double.isInfinite(upperBound) || Double.isNaN(upperBound)) {
470478
return true;
471479
}
472480
assert integerBits == 32 || integerBits == 64 : "Must be int or long " + Assertions.errorMessage(integerBits, upperBound);
473-
long maxValue = NumUtil.maxValue(integerBits);
481+
double maxValue = unsigned
482+
? NumUtil.unsignedToDouble(NumUtil.maxValueUnsigned(integerBits))
483+
: NumUtil.maxValue(integerBits);
474484
/*
475485
* There could be conversion loss here when casting maxValue from long to double - given
476486
* that max can be Long.MAX_VALUE. In order to avoid checking if upperBound is larger than
@@ -482,17 +492,23 @@ private static boolean integralPartLargerMaxValue(FloatStamp floatStamp, int int
482492
* Long.MAX_VALUE) == 9223372036854774784, which fits into long. So if upperBound >=
483493
* (double) maxValue does not hold, i.e., upperBound < (double) maxValue does hold, then
484494
* (long) upperBound will not overflow.
495+
*
496+
* Similarly, for the unsigned case, unsignedToDouble(-1L) is 18446744073709551616 (2**64),
497+
* which is 1 more than the unsigned long max value, i.e. 18446744073709551615 (2**64 - 1).
498+
* So toUnsignedLong(unsignedToDouble(-1L)) will already overflow. The next lower double
499+
* value is Math.nextDown(0x1p64) == 18446744073709549568, which fits into unsigned long.
500+
* The same overflow condition reasoning as for the signed long case applies.
485501
*/
486502
return upperBound >= maxValue;
487503
}
488504

489-
private static boolean integralPartSmallerMinValue(FloatStamp floatStamp, int integerBits) {
505+
private static boolean integralPartSmallerMinValue(FloatStamp floatStamp, int integerBits, boolean unsigned) {
490506
double lowerBound = floatStamp.lowerBound();
491507
if (Double.isInfinite(lowerBound) || Double.isNaN(lowerBound)) {
492508
return true;
493509
}
494510
assert integerBits == 32 || integerBits == 64 : "Must be int or long " + Assertions.errorMessage(integerBits, lowerBound);
495-
long minValue = NumUtil.minValue(integerBits);
511+
double minValue = unsigned ? 0 : NumUtil.minValue(integerBits);
496512
return lowerBound <= minValue;
497513
}
498514

@@ -1677,6 +1693,114 @@ protected Stamp foldStampImpl(Stamp stamp) {
16771693
assert floatStamp.getBits() == 64 : Assertions.errorMessage(floatStamp);
16781694
return StampFactory.forFloat(JavaKind.Float, (float) floatStamp.lowerBound(), (float) floatStamp.upperBound(), floatStamp.isNonNaN());
16791695
}
1696+
},
1697+
1698+
new ArithmeticOpTable.FloatConvertOp(F2UI) {
1699+
1700+
@Override
1701+
public Constant foldConstant(Constant c) {
1702+
PrimitiveConstant value = (PrimitiveConstant) c;
1703+
return JavaConstant.forInt((int) NumUtil.minUnsigned(NumUtil.toUnsignedLong(value.asFloat()), 0xffff_ffffL));
1704+
}
1705+
1706+
@Override
1707+
protected Stamp foldStampImpl(Stamp stamp) {
1708+
if (stamp.isEmpty()) {
1709+
return StampFactory.empty(JavaKind.Int);
1710+
}
1711+
FloatStamp floatStamp = (FloatStamp) stamp;
1712+
assert floatStamp.getBits() == 32 : floatStamp;
1713+
long lowerBound = floatStamp.canBeNaN() ? 0
1714+
: NumUtil.minUnsigned(NumUtil.toUnsignedLong(floatStamp.lowerBound()), 0xffff_ffffL);
1715+
long upperBound = NumUtil.minUnsigned(NumUtil.toUnsignedLong(floatStamp.upperBound()), 0xffff_ffffL);
1716+
return StampFactory.forUnsignedInteger(JavaKind.Int.getBitCount(), lowerBound, upperBound);
1717+
}
1718+
1719+
@Override
1720+
public boolean canOverflowInteger(Stamp inputStamp) {
1721+
return inputStamp instanceof FloatStamp floatStamp && floatingToIntegerCanOverflow(floatStamp, Integer.SIZE, true);
1722+
}
1723+
},
1724+
1725+
new ArithmeticOpTable.FloatConvertOp(F2UL) {
1726+
1727+
@Override
1728+
public Constant foldConstant(Constant c) {
1729+
PrimitiveConstant value = (PrimitiveConstant) c;
1730+
return JavaConstant.forLong(NumUtil.toUnsignedLong(value.asFloat()));
1731+
}
1732+
1733+
@Override
1734+
protected Stamp foldStampImpl(Stamp stamp) {
1735+
if (stamp.isEmpty()) {
1736+
return StampFactory.empty(JavaKind.Long);
1737+
}
1738+
FloatStamp floatStamp = (FloatStamp) stamp;
1739+
assert floatStamp.getBits() == 32 : floatStamp;
1740+
long lowerBound = floatStamp.canBeNaN() ? 0
1741+
: NumUtil.toUnsignedLong(floatStamp.lowerBound());
1742+
long upperBound = NumUtil.toUnsignedLong(floatStamp.upperBound());
1743+
return StampFactory.forUnsignedInteger(JavaKind.Long.getBitCount(), lowerBound, upperBound);
1744+
}
1745+
1746+
@Override
1747+
public boolean canOverflowInteger(Stamp inputStamp) {
1748+
return inputStamp instanceof FloatStamp floatStamp && floatingToIntegerCanOverflow(floatStamp, Long.SIZE, true);
1749+
}
1750+
},
1751+
1752+
new ArithmeticOpTable.FloatConvertOp(D2UI) {
1753+
1754+
@Override
1755+
public Constant foldConstant(Constant c) {
1756+
PrimitiveConstant value = (PrimitiveConstant) c;
1757+
return JavaConstant.forInt((int) NumUtil.minUnsigned(NumUtil.toUnsignedLong(value.asDouble()), 0xffff_ffffL));
1758+
}
1759+
1760+
@Override
1761+
protected Stamp foldStampImpl(Stamp stamp) {
1762+
if (stamp.isEmpty()) {
1763+
return StampFactory.empty(JavaKind.Int);
1764+
}
1765+
FloatStamp floatStamp = (FloatStamp) stamp;
1766+
assert floatStamp.getBits() == 64 : floatStamp;
1767+
long lowerBound = floatStamp.canBeNaN() ? 0
1768+
: NumUtil.minUnsigned(NumUtil.toUnsignedLong(floatStamp.lowerBound()), 0xffff_ffffL);
1769+
long upperBound = NumUtil.minUnsigned(NumUtil.toUnsignedLong(floatStamp.upperBound()), 0xffff_ffffL);
1770+
return StampFactory.forUnsignedInteger(JavaKind.Int.getBitCount(), lowerBound, upperBound);
1771+
}
1772+
1773+
@Override
1774+
public boolean canOverflowInteger(Stamp inputStamp) {
1775+
return inputStamp instanceof FloatStamp floatStamp && floatingToIntegerCanOverflow(floatStamp, Integer.SIZE, true);
1776+
}
1777+
},
1778+
1779+
new ArithmeticOpTable.FloatConvertOp(D2UL) {
1780+
1781+
@Override
1782+
public Constant foldConstant(Constant c) {
1783+
PrimitiveConstant value = (PrimitiveConstant) c;
1784+
return JavaConstant.forLong(NumUtil.toUnsignedLong(value.asDouble()));
1785+
}
1786+
1787+
@Override
1788+
protected Stamp foldStampImpl(Stamp stamp) {
1789+
if (stamp.isEmpty()) {
1790+
return StampFactory.empty(JavaKind.Long);
1791+
}
1792+
FloatStamp floatStamp = (FloatStamp) stamp;
1793+
assert floatStamp.getBits() == 64 : floatStamp;
1794+
long lowerBound = floatStamp.canBeNaN() ? 0
1795+
: NumUtil.toUnsignedLong(floatStamp.lowerBound());
1796+
long upperBound = NumUtil.toUnsignedLong(floatStamp.upperBound());
1797+
return StampFactory.forUnsignedInteger(JavaKind.Long.getBitCount(), lowerBound, upperBound);
1798+
}
1799+
1800+
@Override
1801+
public boolean canOverflowInteger(Stamp inputStamp) {
1802+
return inputStamp instanceof FloatStamp floatStamp && floatingToIntegerCanOverflow(floatStamp, Long.SIZE, true);
1803+
}
16801804
});
16811805

16821806
// Use a separate class to avoid initialization cycles

0 commit comments

Comments
 (0)