Skip to content

Commit 3657e92

Browse files
committedMar 3, 2025
8349637: Integer.numberOfLeadingZeros outputs incorrectly in certain cases
Reviewed-by: thartmann, qamai, jbhateja
1 parent 93c8784 commit 3657e92

File tree

3 files changed

+222
-7
lines changed

3 files changed

+222
-7
lines changed
 

‎src/hotspot/cpu/x86/c2_MacroAssembler_x86.cpp

+10-4
Original file line numberDiff line numberDiff line change
@@ -6227,15 +6227,21 @@ void C2_MacroAssembler::vector_count_leading_zeros_int_avx(XMMRegister dst, XMMR
62276227
// Since IEEE 754 floating point format represents mantissa in 1.0 format
62286228
// hence biased exponent can be used to compute leading zero count as per
62296229
// following formula:-
6230-
// LZCNT = 32 - (biased_exp - 127)
6230+
// LZCNT = 31 - (biased_exp - 127)
62316231
// Special handling has been introduced for Zero, Max_Int and -ve source values.
62326232

62336233
// Broadcast 0xFF
62346234
vpcmpeqd(xtmp1, xtmp1, xtmp1, vec_enc);
62356235
vpsrld(xtmp1, xtmp1, 24, vec_enc);
62366236

6237+
// Remove the bit to the right of the highest set bit ensuring that the conversion to float cannot round up to a higher
6238+
// power of 2, which has a higher exponent than the input. This transformation is valid as only the highest set bit
6239+
// contributes to the leading number of zeros.
6240+
vpsrld(xtmp2, src, 1, vec_enc);
6241+
vpandn(xtmp3, xtmp2, src, vec_enc);
6242+
62376243
// Extract biased exponent.
6238-
vcvtdq2ps(dst, src, vec_enc);
6244+
vcvtdq2ps(dst, xtmp3, vec_enc);
62396245
vpsrld(dst, dst, 23, vec_enc);
62406246
vpand(dst, dst, xtmp1, vec_enc);
62416247

@@ -6244,7 +6250,7 @@ void C2_MacroAssembler::vector_count_leading_zeros_int_avx(XMMRegister dst, XMMR
62446250
// Exponent = biased_exp - 127
62456251
vpsubd(dst, dst, xtmp1, vec_enc);
62466252

6247-
// Exponent = Exponent + 1
6253+
// Exponent_plus_one = Exponent + 1
62486254
vpsrld(xtmp3, xtmp1, 6, vec_enc);
62496255
vpaddd(dst, dst, xtmp3, vec_enc);
62506256

@@ -6257,7 +6263,7 @@ void C2_MacroAssembler::vector_count_leading_zeros_int_avx(XMMRegister dst, XMMR
62576263
vpslld(xtmp1, xtmp3, 5, vec_enc);
62586264
// Exponent is 32 if corresponding source lane contains max_int value.
62596265
vpcmpeqd(xtmp2, dst, xtmp1, vec_enc);
6260-
// LZCNT = 32 - exponent
6266+
// LZCNT = 32 - exponent_plus_one
62616267
vpsubd(dst, xtmp1, dst, vec_enc);
62626268

62636269
// Replace LZCNT with a value 1 if corresponding source lane

‎test/hotspot/jtreg/compiler/vectorization/TestNumberOfContinuousZeros.java

+84-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*
22
* Copyright (c) 2022, 2023, Arm Limited. All rights reserved.
3+
* Copyright (c) 2025, Oracle and/or its affiliates. All rights reserved.
34
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
45
*
56
* This code is free software; you can redistribute it and/or modify it
@@ -23,40 +24,45 @@
2324

2425
/**
2526
* @test
27+
* @bug 8297172 8331993 8349637
2628
* @key randomness
2729
* @summary Test vectorization of numberOfTrailingZeros/numberOfLeadingZeros for Long
2830
* @requires vm.compiler2.enabled
2931
* @requires (os.simpleArch == "x64" & vm.cpu.features ~= ".*avx2.*") |
3032
* (os.simpleArch == "aarch64" & vm.cpu.features ~= ".*sve.*") |
3133
* (os.simpleArch == "riscv64" & vm.cpu.features ~= ".*zvbb.*")
3234
* @library /test/lib /
35+
* @modules jdk.incubator.vector
3336
* @run driver compiler.vectorization.TestNumberOfContinuousZeros
3437
*/
3538

3639
package compiler.vectorization;
3740

41+
import jdk.incubator.vector.*;
3842
import compiler.lib.ir_framework.*;
3943
import java.util.Random;
4044
import jdk.test.lib.Asserts;
45+
import jdk.test.lib.Utils;
4146

4247
public class TestNumberOfContinuousZeros {
48+
private static final int[] SPECIAL = { 0x01FFFFFF, 0x03FFFFFE, 0x07FFFFFC, 0x0FFFFFF8, 0x1FFFFFF0, 0x3FFFFFE0, 0xFFFFFFFF };
4349
private long[] inputLong;
4450
private int[] outputLong;
4551
private int[] inputInt;
4652
private int[] outputInt;
4753
private static final int LEN = 1024;
4854
private Random rng;
4955

50-
public static void main(String args[]) {
51-
TestFramework.run();
56+
public static void main(String[] args) {
57+
TestFramework.runWithFlags("--add-modules=jdk.incubator.vector");
5258
}
5359

5460
public TestNumberOfContinuousZeros() {
5561
inputLong = new long[LEN];
5662
outputLong = new int[LEN];
5763
inputInt = new int[LEN];
5864
outputInt = new int[LEN];
59-
rng = new Random(42);
65+
rng = Utils.getRandomInstance();
6066
for (int i = 0; i < LEN; ++i) {
6167
inputLong[i] = rng.nextLong();
6268
inputInt[i] = rng.nextInt();
@@ -119,5 +125,80 @@ public void checkResultInt() {
119125
Asserts.assertEquals(outputInt[i], Integer.numberOfLeadingZeros(inputInt[i]));
120126
}
121127
}
128+
129+
@Setup
130+
static Object[] setupSpecialIntArray() {
131+
int[] res = new int[LEN];
132+
133+
for (int i = 0; i < LEN; i++) {
134+
res[i] = SPECIAL[i % SPECIAL.length];
135+
}
136+
137+
return new Object[] { res };
138+
}
139+
140+
@Test
141+
@IR(counts = {IRNode.COUNT_LEADING_ZEROS_VI, "> 0"})
142+
@Arguments(setup = "setupSpecialIntArray")
143+
public Object[] testSpecialIntLeadingZeros(int[] ints) {
144+
int[] res = new int[LEN];
145+
146+
for (int i = 0; i < LEN; ++i) {
147+
res[i] = Integer.numberOfLeadingZeros(ints[i]);
148+
}
149+
150+
return new Object[] { ints, res };
151+
}
152+
153+
@Check(test = "testSpecialIntLeadingZeros")
154+
public void checkSpecialIntLeadingZeros(Object[] vals) {
155+
int[] in = (int[]) vals[0];
156+
int[] out = (int[]) vals[1];
157+
158+
for (int i = 0; i < LEN; ++i) {
159+
int value = Integer.numberOfLeadingZeros(in[i]);
160+
161+
if (out[i] != value) {
162+
throw new IllegalStateException("Expected lzcnt(" + in[i] + ") to be " + value + " but got " + out[i]);
163+
}
164+
}
165+
}
166+
167+
private static final VectorSpecies<Integer> SPECIES = IntVector.SPECIES_PREFERRED;
168+
169+
@Test
170+
@IR(counts = {IRNode.COUNT_LEADING_ZEROS_VI, "> 0"})
171+
@Arguments(setup = "setupSpecialIntArray")
172+
public Object[] checkSpecialIntLeadingZerosVector(int[] ints) {
173+
int[] res = new int[LEN];
174+
175+
for (int i = 0; i < ints.length; i += SPECIES.length()) {
176+
IntVector av = IntVector.fromArray(SPECIES, ints, i);
177+
av.lanewise(VectorOperators.LEADING_ZEROS_COUNT).intoArray(res, i);
178+
}
179+
180+
return new Object[] { ints, res };
181+
}
182+
183+
@Check(test = "checkSpecialIntLeadingZerosVector")
184+
public void checkSpecialIntLeadingZerosVector(Object[] vals) {
185+
int[] ints = (int[]) vals[0];
186+
int[] res = (int[]) vals[1];
187+
188+
// Verification
189+
190+
int[] check = new int[LEN];
191+
192+
for (int i = 0; i < ints.length; i += SPECIES.length()) {
193+
IntVector av = IntVector.fromArray(SPECIES, ints, i);
194+
av.lanewise(VectorOperators.LEADING_ZEROS_COUNT).intoArray(check, i);
195+
}
196+
197+
for (int i = 0; i < LEN; i++) {
198+
if (res[i] != check[i]) {
199+
throw new IllegalStateException("Expected " + check[i] + " but got " + res[i]);
200+
}
201+
}
202+
}
122203
}
123204

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
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.
8+
*
9+
* This code is distributed in the hope that it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12+
* version 2 for more details (a copy is included in the LICENSE file that
13+
* accompanied this code).
14+
*
15+
* You should have received a copy of the GNU General Public License version
16+
* 2 along with this work; if not, write to the Free Software Foundation,
17+
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18+
*
19+
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20+
* or visit www.oracle.com if you need additional information or have any
21+
* questions.
22+
*/
23+
24+
/* @test
25+
* @bug 8349637
26+
* @summary Ensure that vectorization of numberOfLeadingZeros and numberOfTrailingZeros outputs correct values
27+
* @library /test/lib /
28+
* @run main/othervm compiler.vectorization.TestVectorZeroCount
29+
*/
30+
31+
package compiler.vectorization;
32+
33+
import java.util.Random;
34+
35+
import jdk.test.lib.Utils;
36+
37+
public class TestVectorZeroCount {
38+
private static final int SIZE = 1024;
39+
private static final Random RANDOM = Utils.getRandomInstance();
40+
41+
private static final int[] INT_VALUES = new int[SIZE];
42+
private static final int[] INT_EXPECTED_LEADING = new int[SIZE];
43+
private static final int[] INT_RESULT_LEADING = new int[SIZE];
44+
private static final int[] INT_EXPECTED_TRAILING = new int[SIZE];
45+
private static final int[] INT_RESULT_TRAILING = new int[SIZE];
46+
47+
private static final long[] LONG_VALUES = new long[SIZE];
48+
private static final long[] LONG_EXPECTED_LEADING = new long[SIZE];
49+
private static final long[] LONG_RESULT_LEADING = new long[SIZE];
50+
private static final long[] LONG_EXPECTED_TRAILING = new long[SIZE];
51+
private static final long[] LONG_RESULT_TRAILING = new long[SIZE];
52+
53+
private static int intCounter = Integer.MIN_VALUE;
54+
private static int longIterations = 100_000_000;
55+
56+
public static boolean testInt() {
57+
boolean done = false;
58+
59+
// Non-vectorized loop as baseline (not vectorized because source array is initialized)
60+
for (int i = 0; i < SIZE; ++i) {
61+
INT_VALUES[i] = intCounter++;
62+
if (intCounter == Integer.MAX_VALUE) {
63+
done = true;
64+
}
65+
INT_EXPECTED_LEADING[i] = Integer.numberOfLeadingZeros(INT_VALUES[i]);
66+
INT_EXPECTED_TRAILING[i] = Integer.numberOfTrailingZeros(INT_VALUES[i]);
67+
}
68+
// Vectorized loop
69+
for (int i = 0; i < SIZE; ++i) {
70+
INT_RESULT_LEADING[i] = Integer.numberOfLeadingZeros(INT_VALUES[i]);
71+
}
72+
for (int i = 0; i < SIZE; ++i) {
73+
INT_RESULT_TRAILING[i] = Integer.numberOfTrailingZeros(INT_VALUES[i]);
74+
}
75+
76+
// Compare results
77+
for (int i = 0; i < SIZE; ++i) {
78+
if (INT_RESULT_LEADING[i] != INT_EXPECTED_LEADING[i]) {
79+
throw new RuntimeException("Unexpected result for Integer.numberOfLeadingZeros(" + INT_VALUES[i] + "): " + INT_RESULT_LEADING[i] + ", expected " + INT_EXPECTED_LEADING[i]);
80+
}
81+
if (INT_RESULT_TRAILING[i] != INT_EXPECTED_TRAILING[i]) {
82+
throw new RuntimeException("Unexpected result for Integer.numberOfTrailingZeros(" + INT_VALUES[i] + "): " + INT_RESULT_TRAILING[i] + ", expected " + INT_EXPECTED_TRAILING[i]);
83+
}
84+
}
85+
return done;
86+
}
87+
88+
public static boolean testLong() {
89+
boolean done = false;
90+
91+
// Non-vectorized loop as baseline (not vectorized because source array is initialized)
92+
for (int i = 0; i < SIZE; ++i) {
93+
// Use random values because the long range is too large to iterate over it
94+
LONG_VALUES[i] = RANDOM.nextLong();
95+
if (longIterations-- == 0) {
96+
done = true;
97+
}
98+
LONG_EXPECTED_LEADING[i] = Long.numberOfLeadingZeros(LONG_VALUES[i]);
99+
LONG_EXPECTED_TRAILING[i] = Long.numberOfTrailingZeros(LONG_VALUES[i]);
100+
}
101+
// Vectorized loop
102+
for (int i = 0; i < SIZE; ++i) {
103+
LONG_RESULT_LEADING[i] = Long.numberOfLeadingZeros(LONG_VALUES[i]);
104+
}
105+
for (int i = 0; i < SIZE; ++i) {
106+
LONG_RESULT_TRAILING[i] = Long.numberOfTrailingZeros(LONG_VALUES[i]);
107+
}
108+
109+
// Compare results
110+
for (int i = 0; i < SIZE; ++i) {
111+
if (LONG_RESULT_LEADING[i] != LONG_EXPECTED_LEADING[i]) {
112+
throw new RuntimeException("Unexpected result for Long.numberOfLeadingZeros(" + LONG_VALUES[i] + "): " + LONG_RESULT_LEADING[i] + ", expected " + LONG_EXPECTED_LEADING[i]);
113+
}
114+
if (LONG_RESULT_TRAILING[i] != LONG_EXPECTED_TRAILING[i]) {
115+
throw new RuntimeException("Unexpected result for Long.numberOfTrailingZeros(" + LONG_VALUES[i] + "): " + LONG_RESULT_TRAILING[i] + ", expected " + LONG_EXPECTED_TRAILING[i]);
116+
}
117+
}
118+
return done;
119+
}
120+
121+
public static void main(String[] args) {
122+
// Run twice to make sure compiled code is used from the beginning
123+
for (int i = 0; i < 2; ++i) {
124+
while (!testLong()) ;
125+
while (!testInt()) ;
126+
}
127+
}
128+
}

0 commit comments

Comments
 (0)
Please sign in to comment.