Skip to content

Commit e4bd1ea

Browse files
committed
[GR-65616] Add unsigned float to integer conversion methods.
PullRequest: graal/21016
2 parents a01c22f + c379e48 commit e4bd1ea

File tree

24 files changed

+1420
-361
lines changed

24 files changed

+1420
-361
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/ExactMathTest.java

Lines changed: 120 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 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
@@ -24,15 +24,17 @@
2424
*/
2525
package jdk.graal.compiler.truffle.test;
2626

27-
import jdk.graal.compiler.nodes.calc.RoundNode;
28-
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
29-
import jdk.graal.compiler.truffle.substitutions.TruffleGraphBuilderPlugins;
27+
import java.util.Arrays;
28+
3029
import org.junit.Assert;
3130
import org.junit.Assume;
3231
import org.junit.Test;
3332

3433
import com.oracle.truffle.api.ExactMath;
3534

35+
import jdk.graal.compiler.nodes.calc.RoundNode;
36+
import jdk.graal.compiler.nodes.graphbuilderconf.InvocationPlugins;
37+
import jdk.graal.compiler.truffle.substitutions.TruffleGraphBuilderPlugins;
3638
import jdk.vm.ci.amd64.AMD64;
3739

3840
public class ExactMathTest extends TruffleCompilerImplTest {
@@ -194,6 +196,96 @@ public void testTruncateDoubleIntrinsified() {
194196
Assert.assertEquals(1, getFinalGraph("truncateFloat").getNodes().filter(RoundNode.class).count());
195197
}
196198

199+
@Test
200+
public void testDoubleToUnsigned() {
201+
for (String methodName : Arrays.asList("truncateDoubleToUnsignedLong", "truncateDoubleToUnsignedInt")) {
202+
test(methodName, Double.NEGATIVE_INFINITY);
203+
test(methodName, -1.0);
204+
test(methodName, Math.nextUp(-1.0));
205+
test(methodName, -Double.MIN_VALUE);
206+
test(methodName, -0.0);
207+
test(methodName, 0.0);
208+
test(methodName, Double.MIN_VALUE);
209+
test(methodName, 0.5);
210+
test(methodName, 1.0);
211+
test(methodName, 1.5);
212+
test(methodName, Math.nextDown(0x1p31));
213+
test(methodName, 0x1p31);
214+
test(methodName, Math.nextDown(0x1p32));
215+
test(methodName, 0x1p32);
216+
test(methodName, 0x1p52 - 1.0);
217+
test(methodName, 0x1p52 - 0.5); // largest representable non-integral value
218+
test(methodName, 0x1p52);
219+
test(methodName, 0x1p53 - 1.0); // largest exactly representable integral value
220+
test(methodName, 0x1p53);
221+
test(methodName, 0x1p63);
222+
test(methodName, Math.nextDown(0x1p64));
223+
test(methodName, 0x1p64);
224+
test(methodName, Double.MAX_VALUE);
225+
test(methodName, Double.POSITIVE_INFINITY);
226+
test(methodName, Double.NaN);
227+
test(methodName, Double.longBitsToDouble(0x7ff0000000000001L)); // signaling NaN
228+
}
229+
}
230+
231+
@Test
232+
public void testFloatToUnsigned() {
233+
for (String methodName : Arrays.asList("truncateFloatToUnsignedLong", "truncateFloatToUnsignedInt")) {
234+
test(methodName, Float.NEGATIVE_INFINITY);
235+
test(methodName, -1.0f);
236+
test(methodName, Math.nextUp(-1.0f));
237+
test(methodName, -Float.MIN_VALUE);
238+
test(methodName, -0.0f);
239+
test(methodName, 0.0f);
240+
test(methodName, Float.MIN_VALUE);
241+
test(methodName, 0.5f);
242+
test(methodName, 1.0f);
243+
test(methodName, 1.5f);
244+
test(methodName, 0x1p23f - 1.0f);
245+
test(methodName, 0x1p23f - 0.5f); // largest representable non-integral value
246+
test(methodName, 0x1p23f);
247+
test(methodName, 0x1p24f - 1.0f); // largest exactly representable integral value
248+
test(methodName, 0x1p24f);
249+
test(methodName, Math.nextDown(0x1p31f));
250+
test(methodName, 0x1p31f);
251+
test(methodName, Math.nextDown(0x1p32f));
252+
test(methodName, 0x1p32f);
253+
test(methodName, 0x1p63f);
254+
test(methodName, Math.nextDown(0x1p64f));
255+
test(methodName, 0x1p64f);
256+
test(methodName, Float.MAX_VALUE);
257+
test(methodName, Float.POSITIVE_INFINITY);
258+
test(methodName, Float.NaN);
259+
test(methodName, Float.intBitsToFloat(0x7f800001)); // signaling NaN
260+
}
261+
}
262+
263+
@Test
264+
public void testUnsignedLongToFloat() {
265+
for (String methodName : Arrays.asList("unsignedLongToFloat", "unsignedLongToDouble")) {
266+
test(methodName, 0L);
267+
test(methodName, 1L);
268+
test(methodName, (long) Integer.MAX_VALUE);
269+
test(methodName, 1L << 31);
270+
test(methodName, (1L << 32) - 1L);
271+
test(methodName, 1L << 32);
272+
test(methodName, 0x0020000020000000L);
273+
test(methodName, 0x0020000020000001L);
274+
test(methodName, 0x7fffffbfffffffffL);
275+
test(methodName, 0x7fffffc000000000L);
276+
test(methodName, 0x7ffffffffffffdffL);
277+
test(methodName, 0x7ffffffffffffc00L);
278+
test(methodName, Long.MAX_VALUE);
279+
test(methodName, Long.MIN_VALUE);
280+
test(methodName, 0x8000008000000001L);
281+
test(methodName, 0xfffffe8000000001L);
282+
test(methodName, 0xfffffe8000000002L);
283+
test(methodName, 0xfffffffffffff401L);
284+
test(methodName, 0xfffffffffffff402L);
285+
test(methodName, 0xffffffffffffffffL);
286+
}
287+
}
288+
197289
public static int add(int a, int b) {
198290
return Math.addExact(a, b);
199291
}
@@ -273,4 +365,28 @@ public static float truncateFloat(float a) {
273365
public static double truncateDouble(double a) {
274366
return ExactMath.truncate(a);
275367
}
368+
369+
public static long truncateDoubleToUnsignedLong(double x) {
370+
return ExactMath.truncateToUnsignedLong(x);
371+
}
372+
373+
public static long truncateFloatToUnsignedLong(float x) {
374+
return ExactMath.truncateToUnsignedLong(x);
375+
}
376+
377+
public static int truncateDoubleToUnsignedInt(double x) {
378+
return ExactMath.truncateToUnsignedInt(x);
379+
}
380+
381+
public static int truncateFloatToUnsignedInt(float x) {
382+
return ExactMath.truncateToUnsignedInt(x);
383+
}
384+
385+
public static double unsignedLongToDouble(long x) {
386+
return ExactMath.unsignedToDouble(x);
387+
}
388+
389+
public static float unsignedLongToFloat(long x) {
390+
return ExactMath.unsignedToFloat(x);
391+
}
276392
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64ASIMDAssembler.java

Lines changed: 86 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2021, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2021, 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
@@ -625,16 +625,20 @@ public enum ASIMDInstruction {
625625
CMLT_ZERO(0b01010 << 12),
626626
ABS(0b01011 << 12),
627627
XTN(0b10010 << 12),
628+
SQXTN(0b10100 << 12),
629+
UQXTN(UBit | 0b10100 << 12),
628630
/* size 0x */
629631
FCVTN(0b10110 << 12),
630632
FCVTL(0b10111 << 12),
631633
SCVTF(0b11101 << 12),
634+
UCVTF(UBit | 0b11101 << 12),
632635
/* size 1x */
633636
FCMGT_ZERO(0b01100 << 12),
634637
FCMEQ_ZERO(0b01101 << 12),
635638
FCMLT_ZERO(0b01110 << 12),
636639
FABS(0b01111 << 12),
637640
FCVTZS(0b11011 << 12),
641+
FCVTZU(UBit | 0b11011 << 12),
638642
/* UBit 1, size xx */
639643
REV32(UBit | 0b00000 << 12),
640644
CMGE_ZERO(UBit | 0b01000 << 12),
@@ -1961,7 +1965,7 @@ public void fcvtnVV(ElementSize srcESize, Register dst, Register src) {
19611965
}
19621966

19631967
/**
1964-
* C7.2.90 Floating-point convert to to signed integer, rounding toward zero.<br>
1968+
* C7.2.90 Floating-point convert to signed integer, rounding toward zero.<br>
19651969
*
19661970
* @param size register size.
19671971
* @param eSize source element size. Must be ElementSize.Word or ElementSize.DoubleWord.
@@ -1979,6 +1983,25 @@ public void fcvtzsVV(ASIMDSize size, ElementSize eSize, Register dst, Register s
19791983
twoRegMiscEncoding(ASIMDInstruction.FCVTZS, size, elemSize1X(eSize), dst, src);
19801984
}
19811985

1986+
/**
1987+
* Floating-point convert to unsigned integer, rounding toward zero.<br>
1988+
*
1989+
* @param size register size.
1990+
* @param eSize source element size. Must be ElementSize.Word or ElementSize.DoubleWord.
1991+
* ElementSize.DoubleWord is only applicable when size is 128 (i.e. the operation is
1992+
* performed on more than one element).
1993+
* @param dst SIMD register.
1994+
* @param src SIMD register.
1995+
*/
1996+
public void fcvtzuVV(ASIMDSize size, ElementSize eSize, Register dst, Register src) {
1997+
assert usesMultipleLanes(size, eSize) : "Must use multiple lanes " + size + " " + eSize;
1998+
assert dst.getRegisterCategory().equals(SIMD) : dst;
1999+
assert src.getRegisterCategory().equals(SIMD) : src;
2000+
assert eSize == ElementSize.Word || eSize == ElementSize.DoubleWord : eSize;
2001+
2002+
twoRegMiscEncoding(ASIMDInstruction.FCVTZU, size, elemSize1X(eSize), dst, src);
2003+
}
2004+
19822005
/**
19832006
* C7.2.97 floating point divide vector.<br>
19842007
*
@@ -2734,6 +2757,25 @@ public void scvtfVV(ASIMDSize size, ElementSize eSize, Register dst, Register sr
27342757
twoRegMiscEncoding(ASIMDInstruction.SCVTF, size, elemSize0X(eSize), dst, src);
27352758
}
27362759

2760+
/**
2761+
* Unsigned integer convert to floating-point.<br>
2762+
*
2763+
* @param size register size.
2764+
* @param eSize source element size. Must be ElementSize.Word or ElementSize.DoubleWord.
2765+
* ElementSize.DoubleWord is only applicable when size is 128 (i.e. the operation is
2766+
* performed on more than one element).
2767+
* @param dst SIMD register.
2768+
* @param src SIMD register.
2769+
*/
2770+
public void ucvtfVV(ASIMDSize size, ElementSize eSize, Register dst, Register src) {
2771+
assert usesMultipleLanes(size, eSize) : "Must use multiple lanes " + size + " " + eSize;
2772+
assert dst.getRegisterCategory().equals(SIMD) : dst;
2773+
assert src.getRegisterCategory().equals(SIMD) : src;
2774+
assert eSize == ElementSize.Word || eSize == ElementSize.DoubleWord : eSize;
2775+
2776+
twoRegMiscEncoding(ASIMDInstruction.UCVTF, size, elemSize0X(eSize), dst, src);
2777+
}
2778+
27372779
/**
27382780
* C7.2.239 SHA1 hash update.<br>
27392781
*
@@ -4096,6 +4138,48 @@ public void xtn2VV(ElementSize dstESize, Register dst, Register src) {
40964138
twoRegMiscEncoding(ASIMDInstruction.XTN, true, elemSizeXX(dstESize), dst, src);
40974139
}
40984140

4141+
/**
4142+
* Signed saturating extract Narrow.<br>
4143+
* <p>
4144+
* From the manual: "This instruction reads each vector element from the source SIMD register,
4145+
* saturates each value to half the original width, places the result into a vector, and writes
4146+
* the vector to the destination SIMD register. All the values in this instruction are signed
4147+
* integer values."
4148+
*
4149+
* @param dstESize destination element size. Cannot be ElementSize.DoubleWord. The source
4150+
* element size is twice this width.
4151+
* @param dst SIMD register.
4152+
* @param src SIMD register.
4153+
*/
4154+
public void sqxtnVV(ElementSize dstESize, Register dst, Register src) {
4155+
assert dst.getRegisterCategory().equals(SIMD) : dst;
4156+
assert src.getRegisterCategory().equals(SIMD) : src;
4157+
assert dstESize != ElementSize.DoubleWord : dstESize;
4158+
4159+
twoRegMiscEncoding(ASIMDInstruction.SQXTN, false, elemSizeXX(dstESize), dst, src);
4160+
}
4161+
4162+
/**
4163+
* Unsigned saturating extract Narrow.<br>
4164+
* <p>
4165+
* From the manual: "This instruction reads each vector element from the source SIMD register,
4166+
* saturates each value to half the original width, places the result into a vector, and writes
4167+
* the vector to the destination SIMD register. All the values in this instruction are unsigned
4168+
* integer values."
4169+
*
4170+
* @param dstESize destination element size. Cannot be ElementSize.DoubleWord. The source
4171+
* element size is twice this width.
4172+
* @param dst SIMD register.
4173+
* @param src SIMD register.
4174+
*/
4175+
public void uqxtnVV(ElementSize dstESize, Register dst, Register src) {
4176+
assert dst.getRegisterCategory().equals(SIMD) : dst;
4177+
assert src.getRegisterCategory().equals(SIMD) : src;
4178+
assert dstESize != ElementSize.DoubleWord : dstESize;
4179+
4180+
twoRegMiscEncoding(ASIMDInstruction.UQXTN, false, elemSizeXX(dstESize), dst, src);
4181+
}
4182+
40994183
/**
41004184
* C7.2.403 Zip vectors (primary).
41014185
* <p>

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/asm/aarch64/AArch64Assembler.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@
9797
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FCVTAS;
9898
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FCVTMS;
9999
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FCVTZS;
100+
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FCVTZU;
100101
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FDIV;
101102
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FMADD;
102103
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.FMAX;
@@ -151,6 +152,7 @@
151152
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.TBNZ;
152153
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.TBZ;
153154
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.UBFM;
155+
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.UCVTF;
154156
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.Instruction.UDIV;
155157
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.InstructionType.FP32;
156158
import static jdk.graal.compiler.asm.aarch64.AArch64Assembler.InstructionType.FP64;
@@ -1013,7 +1015,9 @@ public enum Instruction {
10131015
FCVTMS(0x00100000),
10141016

10151017
FCVTZS(0x00180000),
1018+
FCVTZU(0x00190000),
10161019
SCVTF(0x00020000),
1020+
UCVTF(0x00030000),
10171021

10181022
FABS(0x00008000),
10191023
FSQRT(0x00018000),
@@ -3524,6 +3528,20 @@ public void fcvtzs(int dstSize, int srcSize, Register dst, Register src) {
35243528
fcvtCpuFpuInstruction(FCVTZS, dst, src, generalFromSize(dstSize), floatFromSize(srcSize));
35253529
}
35263530

3531+
/**
3532+
* Floating-point Convert to Unsigned integer, rounding toward Zero.
3533+
*
3534+
* @param dstSize size of integer register. 32 or 64.
3535+
* @param srcSize size of floating point register. 32 or 64.
3536+
* @param dst general purpose register. May not be null, the zero-register or the stackpointer.
3537+
* @param src floating point register. May not be null.
3538+
*/
3539+
public void fcvtzu(int dstSize, int srcSize, Register dst, Register src) {
3540+
assert verifySizesAndRegistersRF(dstSize, srcSize, dst, src);
3541+
3542+
fcvtCpuFpuInstruction(FCVTZU, dst, src, generalFromSize(dstSize), floatFromSize(srcSize));
3543+
}
3544+
35273545
/* Convert from Integer (5.7.4.2) */
35283546
/**
35293547
* C7.2.236 Signed integer Convert to Floating-point (scalar).
@@ -3539,6 +3557,20 @@ public void scvtf(int dstSize, int srcSize, Register dst, Register src) {
35393557
fcvtCpuFpuInstruction(SCVTF, dst, src, floatFromSize(dstSize), generalFromSize(srcSize));
35403558
}
35413559

3560+
/**
3561+
* Unsigned integer Convert to Floating-point (scalar).
3562+
*
3563+
* @param dstSize size of floating point register. 32 or 64.
3564+
* @param srcSize size of integer register. 32 or 64.
3565+
* @param dst floating point register. May not be null.
3566+
* @param src general purpose register. May not be null or the stackpointer.
3567+
*/
3568+
public void ucvtf(int dstSize, int srcSize, Register dst, Register src) {
3569+
assert verifySizesAndRegistersFZ(dstSize, srcSize, dst, src);
3570+
3571+
fcvtCpuFpuInstruction(UCVTF, dst, src, floatFromSize(dstSize), generalFromSize(srcSize));
3572+
}
3573+
35423574
private void fcvtCpuFpuInstruction(Instruction instr, Register dst, Register src, InstructionType type1, InstructionType type2) {
35433575
emitInt(type1.encoding | type2.encoding | instr.encoding | FpConvertOp | rd(dst) | rs1(src));
35443576
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/core/aarch64/AArch64ArithmeticLIRGenerator.java

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 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
@@ -286,24 +286,13 @@ protected Value emitMultiplyAddSub(AArch64ArithmeticOp op, Value a, Value b, Val
286286
}
287287

288288
protected static AArch64Kind getFloatConvertResultKind(FloatConvert op) {
289-
switch (op) {
290-
case F2I:
291-
case D2I:
292-
return AArch64Kind.DWORD;
293-
case F2L:
294-
case D2L:
295-
return AArch64Kind.QWORD;
296-
case I2F:
297-
case L2F:
298-
case D2F:
299-
return AArch64Kind.SINGLE;
300-
case I2D:
301-
case L2D:
302-
case F2D:
303-
return AArch64Kind.DOUBLE;
304-
default:
305-
throw GraalError.shouldNotReachHereUnexpectedValue(op); // ExcludeFromJacocoGeneratedReport
306-
}
289+
return switch (op) {
290+
case F2I, D2I, F2UI, D2UI -> AArch64Kind.DWORD;
291+
case F2L, D2L, F2UL, D2UL -> AArch64Kind.QWORD;
292+
case D2F, I2F, L2F, UI2F, UL2F -> AArch64Kind.SINGLE;
293+
case F2D, I2D, L2D, UI2D, UL2D -> AArch64Kind.DOUBLE;
294+
default -> throw GraalError.shouldNotReachHereUnexpectedValue(op); // ExcludeFromJacocoGeneratedReport
295+
};
307296
}
308297

309298
@Override

0 commit comments

Comments
 (0)