1 /*
2 * Copyright (c) 2022, 2023, Arm Limited. All rights reserved.
3 * Copyright (c) 2024, 2026, Oracle and/or its affiliates. All rights reserved.
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 *
6 * This code is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License version 2 only, as
8 * published by the Free Software Foundation.
9 *
10 * This code is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 * version 2 for more details (a copy is included in the LICENSE file that
14 * accompanied this code).
15 *
16 * You should have received a copy of the GNU General Public License version
17 * 2 along with this work; if not, write to the Free Software Foundation,
18 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
19 *
20 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
21 * or visit www.oracle.com if you need additional information or have any
22 * questions.
23 */
24
25 /*
26 * @test
27 * @bug 8183390 8340010 8342095
28 * @summary Vectorization test on array type conversions
29 * @library /test/lib /
30 *
31 * @build jdk.test.whitebox.WhiteBox
32 * compiler.vectorization.runner.VectorizationTestRunner
33 *
34 * @requires vm.compiler2.enabled
35 *
36 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
37 *
38 * @run main/othervm -Xbootclasspath/a:.
39 * -XX:+UnlockDiagnosticVMOptions
40 * -XX:+WhiteBoxAPI
41 * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_nAV
42 *
43 * @run main/othervm -Xbootclasspath/a:.
44 * -XX:+UnlockDiagnosticVMOptions
45 * -XX:+WhiteBoxAPI
46 * compiler.vectorization.runner.ArrayTypeConvertTest nCOH_yAV
47 *
48 * @run main/othervm -Xbootclasspath/a:.
49 * -XX:+UnlockDiagnosticVMOptions
50 * -XX:+WhiteBoxAPI
51 * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_nAV
52 *
53 * @run main/othervm -Xbootclasspath/a:.
54 * -XX:+UnlockDiagnosticVMOptions
55 * -XX:+WhiteBoxAPI
56 * compiler.vectorization.runner.ArrayTypeConvertTest yCOH_yAV
57 */
58
59 package compiler.vectorization.runner;
60
61 import compiler.lib.ir_framework.*;
62
63 // Explanation about AlignVector: we require 8-byte alignment of all addresses.
64 // But the array base offset changes with UseCompactObjectHeaders.
65 // This means it affects the alignment constraints.
66
67 public class ArrayTypeConvertTest extends VectorizationTestRunner {
68
69 // We must pass the flags directly to the test-VM, and not the driver vm in the @run above.
70 @Override
71 protected String[] testVMFlags(String[] args) {
72 return switch (args[0]) {
73 case "nCOH_nAV" -> new String[]{"-XX:-UseCompactObjectHeaders", "-XX:-AlignVector"};
74 case "nCOH_yAV" -> new String[]{"-XX:-UseCompactObjectHeaders", "-XX:+AlignVector"};
75 case "yCOH_nAV" -> new String[]{"-XX:+UseCompactObjectHeaders", "-XX:-AlignVector"};
76 case "yCOH_yAV" -> new String[]{"-XX:+UseCompactObjectHeaders", "-XX:+AlignVector"};
77 default -> { throw new RuntimeException("Test argument not recognized: " + args[0]); }
78 };
79 }
80
81 private static final int SIZE = 543;
82
83 private byte[] bytes;
84 private short[] shorts;
85 private char[] chars;
86 private int[] ints;
87 private long[] longs;
88 private float[] floats;
89 private double[] doubles;
90
91 public ArrayTypeConvertTest() {
92 bytes = new byte[SIZE];
93 shorts = new short[SIZE];
94 chars = new char[SIZE];
95 ints = new int[SIZE];
96 longs = new long[SIZE];
97 floats = new float[SIZE];
98 doubles = new double[SIZE];
99 for (int i = 0; i < SIZE; i++) {
100 bytes[i] = (byte) (-i / 128);
101 shorts[i] = (short) (i / 3 - 12345);
102 chars[i] = (char) (i * 2);
103 ints[i] = -22 * i;
104 longs[i] = -258L * i + 99L;
105 floats[i] = (float) (i * 2.498f);
106 doubles[i] = -3 * i;
107 }
108 }
109
110 // ---------------- Integer Extension ----------------
111 @Test
112 @IR(applyIfCPUFeature = { "avx", "true" },
113 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
114 counts = { IRNode.VECTOR_CAST_S2I, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
115 public int[] signExtension() {
116 int[] res = new int[SIZE];
117 for (int i = 0; i < SIZE; i++) {
118 res[i] = shorts[i];
119 }
120 return res;
121 }
122
123 @Test
124 @IR(failOn = {IRNode.STORE_VECTOR})
125 // Subword vector casts with char do not work currently, see JDK-8349562.
126 // Assert the vectorization failure so that we are reminded to update
127 // the test when this limitation is addressed in the future.
128 public int[] zeroExtension() {
129 int[] res = new int[SIZE];
130 for (int i = 0; i < SIZE; i++) {
131 res[i] = chars[i];
132 }
133 return res;
134 }
135
136 @Test
137 @IR(applyIfCPUFeature = { "avx", "true" },
138 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
139 counts = { IRNode.VECTOR_CAST_B2I, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
140 public int[] signExtensionFromByte() {
141 int[] res = new int[SIZE];
142 for (int i = 0; i < SIZE; i++) {
143 res[i] = bytes[i];
144 }
145 return res;
146 }
147
148 @Test
149 @IR(applyIfCPUFeature = { "avx", "true" },
150 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
151 counts = { IRNode.VECTOR_CAST_B2S, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" })
152 public short[] signExtensionFromByteToShort() {
153 short[] res = new short[SIZE];
154 for (int i = 0; i < SIZE; i++) {
155 res[i] = bytes[i];
156 }
157 return res;
158 }
159
160 // ---------------- Integer Narrow ----------------
161 @Test
162 @IR(applyIfCPUFeature = { "avx", "true" },
163 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
164 counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_short)", ">0" })
165 public short[] narrowToSigned() {
166 short[] res = new short[SIZE];
167 for (int i = 0; i < SIZE; i++) {
168 res[i] = (short) ints[i];
169 }
170 return res;
171 }
172
173 @Test
174 @IR(applyIfCPUFeature = { "avx", "true" },
175 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
176 counts = { IRNode.VECTOR_CAST_I2S, IRNode.VECTOR_SIZE + "min(max_int, max_char)", ">0" })
177 public char[] narrowToUnsigned() {
178 char[] res = new char[SIZE];
179 for (int i = 0; i < SIZE; i++) {
180 res[i] = (char) ints[i];
181 }
182 return res;
183 }
184
185 @Test
186 @IR(applyIfCPUFeature = { "avx", "true" },
187 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
188 counts = { IRNode.VECTOR_CAST_I2B, IRNode.VECTOR_SIZE + "min(max_int, max_byte)", ">0" })
189 public byte[] narrowToByte() {
190 byte[] res = new byte[SIZE];
191 for (int i = 0; i < SIZE; i++) {
192 res[i] = (byte) ints[i];
193 }
194 return res;
195 }
196
197 @Test
198 @IR(applyIfCPUFeature = { "avx", "true" },
199 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
200 counts = { IRNode.VECTOR_CAST_S2B, IRNode.VECTOR_SIZE + "min(max_short, max_byte)", ">0" })
201 public byte[] narrowShortToByte() {
202 byte[] res = new byte[SIZE];
203 for (int i = 0; i < SIZE; i++) {
204 res[i] = (byte) shorts[i];
205 }
206 return res;
207 }
208
209 // ---------------- Convert I/L to F/D ----------------
210 @Test
211 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
212 counts = {IRNode.VECTOR_CAST_I2F, IRNode.VECTOR_SIZE + "min(max_int, max_float)", ">0"})
213 public float[] convertIntToFloat() {
214 float[] res = new float[SIZE];
215 for (int i = 0; i < SIZE; i++) {
216 res[i] = (float) ints[i];
217 }
218 return res;
219 }
220
221 @Test
222 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
223 counts = {IRNode.VECTOR_CAST_I2D, IRNode.VECTOR_SIZE + "min(max_int, max_double)", ">0"})
224 public double[] convertIntToDouble() {
225 double[] res = new double[SIZE];
226 for (int i = 0; i < SIZE; i++) {
227 res[i] = (double) ints[i];
228 }
229 return res;
230 }
231
232 @Test
233 @IR(applyIfCPUFeatureOr = {"sve", "true", "avx512dq", "true", "rvv", "true"},
234 counts = {IRNode.VECTOR_CAST_L2F, IRNode.VECTOR_SIZE + "min(max_long, max_float)", ">0"})
235 public float[] convertLongToFloat() {
236 float[] res = new float[SIZE];
237 for (int i = 0; i < SIZE; i++) {
238 res[i] = (float) longs[i];
239 }
240 return res;
241 }
242
243 @Test
244 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx512dq", "true", "rvv", "true"},
245 counts = {IRNode.VECTOR_CAST_L2D, IRNode.VECTOR_SIZE + "min(max_long, max_double)", ">0"})
246 public double[] convertLongToDouble() {
247 double[] res = new double[SIZE];
248 for (int i = 0; i < SIZE; i++) {
249 res[i] = (double) longs[i];
250 }
251 return res;
252 }
253
254 // ---------------- Convert Subword-I to F/D ----------------
255 @Test
256 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx2", "true", "rvv", "true"},
257 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
258 counts = {IRNode.VECTOR_CAST_S2F, IRNode.VECTOR_SIZE + "min(max_short, max_float)", ">0"})
259 public float[] convertShortToFloat() {
260 float[] res = new float[SIZE];
261 for (int i = 0; i < SIZE; i++) {
262 res[i] = (float) shorts[i];
263 // AlignVector=true requires that all vector load/store are 8-byte aligned.
264 // F_adr = base + UNSAFE.ARRAY_FLOAT_BASE_OFFSET + 4*i
265 // = 16 (UseCompactObjectHeaders=false) -> i % 2 = 0
266 // = 12 (UseCompactObjectHeaders=true ) -> i % 2 = 1
267 // S_adr = base + UNSAFE.ARRAY_SHORT_BASE_OFFSET + 2*i
268 // = 16 (UseCompactObjectHeaders=false) -> i % 4 = 0 -> can align both
269 // = 12 (UseCompactObjectHeaders=true ) -> i % 4 = 2 -> cannot align both
270 }
271 return res;
272 }
273
274 @Test
275 @IR(applyIfCPUFeature = {"rvv", "true"},
276 applyIf = {"MaxVectorSize", ">=32"},
277 counts = {IRNode.VECTOR_CAST_S2D, IRNode.VECTOR_SIZE + "min(max_short, max_double)", ">0"})
278 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true"},
279 applyIf = {"MaxVectorSize", ">=16"},
280 counts = {IRNode.VECTOR_CAST_S2D, IRNode.VECTOR_SIZE + "min(max_short, max_double)", ">0"})
281 public double[] convertShortToDouble() {
282 double[] res = new double[SIZE];
283 for (int i = 0; i < SIZE; i++) {
284 res[i] = (double) shorts[i];
285 }
286 return res;
287 }
288
289 @Test
290 @IR(failOn = {IRNode.STORE_VECTOR})
291 // Subword vector casts with char do not work currently, see JDK-8349562.
292 // Assert the vectorization failure so that we are reminded to update
293 // the test when this limitation is addressed in the future.
294 public float[] convertCharToFloat() {
295 float[] res = new float[SIZE];
296 for (int i = 0; i < SIZE; i++) {
297 res[i] = (float) chars[i];
298 }
299 return res;
300 }
301
302 @Test
303 @IR(failOn = {IRNode.STORE_VECTOR})
304 // Subword vector casts with char do not work currently, see JDK-8349562.
305 // Assert the vectorization failure so that we are reminded to update
306 // the test when this limitation is addressed in the future.
307 public double[] convertCharToDouble() {
308 double[] res = new double[SIZE];
309 for (int i = 0; i < SIZE; i++) {
310 res[i] = (double) chars[i];
311 }
312 return res;
313 }
314
315 // ---------------- Convert F/D to I/L ----------------
316 @Test
317 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "avx10_2", "true", "rvv", "true"},
318 counts = {IRNode.VECTOR_CAST_F2I, IRNode.VECTOR_SIZE + "min(max_float, max_int)", "> 0"})
319 @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
320 applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
321 @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
322 applyIfCPUFeature = {"avx10_2", "true"})
323 public int[] convertFloatToInt() {
324 int[] res = new int[SIZE];
325 for (int i = 0; i < SIZE; i++) {
326 res[i] = (int) floats[i];
327 }
328 return res;
329 }
330
331 @Test
332 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx512dq", "true", "avx10_2", "true", "rvv", "true"},
333 counts = {IRNode.VECTOR_CAST_F2L, IRNode.VECTOR_SIZE + "min(max_float, max_long)", "> 0"})
334 @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
335 applyIfCPUFeatureAnd = {"avx512dq", "true", "avx10_2", "false"})
336 @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
337 applyIfCPUFeature = {"avx10_2", "true"})
338 public long[] convertFloatToLong() {
339 long[] res = new long[SIZE];
340 for (int i = 0; i < SIZE; i++) {
341 res[i] = (long) floats[i];
342 }
343 return res;
344 }
345
346 @Test
347 @IR(applyIfCPUFeatureOr = {"sve", "true", "avx", "true", "avx10_2", "true", "rvv", "true"},
348 counts = {IRNode.VECTOR_CAST_D2I, IRNode.VECTOR_SIZE + "min(max_double, max_int)", "> 0"})
349 @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
350 applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
351 @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
352 applyIfCPUFeature = {"avx10_2", "true"})
353 public int[] convertDoubleToInt() {
354 int[] res = new int[SIZE];
355 for (int i = 0; i < SIZE; i++) {
356 res[i] = (int) doubles[i];
357 }
358 return res;
359 }
360
361 @Test
362 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx512dq", "true", "avx10_2", "true", "rvv", "true"},
363 counts = {IRNode.VECTOR_CAST_D2L, IRNode.VECTOR_SIZE + "min(max_double, max_long)", "> 0"})
364 @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
365 applyIfCPUFeatureAnd = {"avx512dq", "true", "avx10_2", "false"})
366 @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
367 applyIfCPUFeature = {"avx10_2", "true"})
368 public long[] convertDoubleToLong() {
369 long[] res = new long[SIZE];
370 for (int i = 0; i < SIZE; i++) {
371 res[i] = (long) doubles[i];
372 }
373 return res;
374 }
375
376 // ---------------- Convert F/D to Subword-I ----------------
377 @Test
378 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx2", "true", "avx10_2", "true", "rvv", "true"},
379 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
380 counts = {IRNode.VECTOR_CAST_F2S, IRNode.VECTOR_SIZE + "min(max_float, max_short)", "> 0"})
381 @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
382 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
383 applyIfCPUFeatureAnd = {"avx2", "true", "avx10_2", "false"})
384 @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
385 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
386 applyIfCPUFeature = {"avx10_2", "true"})
387 public short[] convertFloatToShort() {
388 short[] res = new short[SIZE];
389 for (int i = 0; i < SIZE; i++) {
390 res[i] = (short) floats[i];
391 // AlignVector=true requires that all vector load/store are 8-byte aligned.
392 // F_adr = base + UNSAFE.ARRAY_FLOAT_BASE_OFFSET + 4*i
393 // = 16 (UseCompactObjectHeaders=false) -> i % 2 = 0
394 // = 12 (UseCompactObjectHeaders=true ) -> i % 2 = 1
395 // S_adr = base + UNSAFE.ARRAY_SHORT_BASE_OFFSET + 2*i
396 // = 16 (UseCompactObjectHeaders=false) -> i % 4 = 0 -> can align both
397 // = 12 (UseCompactObjectHeaders=true ) -> i % 4 = 2 -> cannot align both
398 }
399 return res;
400 }
401
402 @Test
403 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx2", "true", "avx10_2", "true", "rvv", "true"},
404 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
405 counts = {IRNode.VECTOR_CAST_F2S, IRNode.VECTOR_SIZE + "min(max_float, max_char)", "> 0"})
406 @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
407 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
408 applyIfCPUFeatureAnd = {"avx2", "true", "avx10_2", "false"})
409 @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
410 applyIfOr = {"AlignVector", "false", "UseCompactObjectHeaders", "false"},
411 applyIfCPUFeature = {"avx10_2", "true"})
412 public char[] convertFloatToChar() {
413 char[] res = new char[SIZE];
414 for (int i = 0; i < SIZE; i++) {
415 res[i] = (char) floats[i];
416 // AlignVector=true requires that all vector load/store are 8-byte aligned.
417 // F_adr = base + UNSAFE.ARRAY_FLOAT_BASE_OFFSET + 4*i
418 // = 16 (UseCompactObjectHeaders=false) -> i % 2 = 0
419 // = 12 (UseCompactObjectHeaders=true ) -> i % 2 = 1
420 // S_adr = base + UNSAFE.ARRAY_SHORT_BASE_OFFSET + 2*i
421 // = 16 (UseCompactObjectHeaders=false) -> i % 4 = 0 -> can align both
422 // = 12 (UseCompactObjectHeaders=true ) -> i % 4 = 2 -> cannot align both
423 }
424 return res;
425 }
426
427 @Test
428 @IR(applyIfCPUFeature = {"rvv", "true"},
429 applyIf = {"MaxVectorSize", ">=32"},
430 counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_short)", "> 0"})
431 @IR(applyIfCPUFeatureOr = {"sve", "true", "avx", "true", "avx10_2", "true"},
432 applyIf = {"MaxVectorSize", ">=16"},
433 counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_short)", "> 0"})
434 @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
435 applyIf = {"MaxVectorSize", ">=16"},
436 applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
437 @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
438 applyIf = {"MaxVectorSize", ">=16"},
439 applyIfCPUFeature = {"avx10_2", "true"})
440 public short[] convertDoubleToShort() {
441 short[] res = new short[SIZE];
442 for (int i = 0; i < SIZE; i++) {
443 res[i] = (short) doubles[i];
444 }
445 return res;
446 }
447
448 @Test
449 @IR(applyIfCPUFeature = {"rvv", "true"},
450 applyIf = {"MaxVectorSize", ">= 32"},
451 counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_char)", "> 0"})
452 @IR(applyIfCPUFeatureOr = {"sve", "true", "avx", "true", "avx10_2", "true"},
453 applyIf = {"MaxVectorSize", ">= 16"},
454 counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_char)", "> 0"})
455 @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
456 applyIf = {"MaxVectorSize", ">=16"},
457 applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
458 @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
459 applyIf = {"MaxVectorSize", ">=16"},
460 applyIfCPUFeature = {"avx10_2", "true"})
461 public char[] convertDoubleToChar() {
462 char[] res = new char[SIZE];
463 for (int i = 0; i < SIZE; i++) {
464 res[i] = (char) doubles[i];
465 }
466 return res;
467 }
468
469 // ---------------- Convert Between F & D ----------------
470 @Test
471 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
472 counts = {IRNode.VECTOR_CAST_F2D, IRNode.VECTOR_SIZE + "min(max_float, max_double)", ">0"})
473 public double[] convertFloatToDouble() {
474 double[] res = new double[SIZE];
475 for (int i = 0; i < SIZE; i++) {
476 res[i] = (double) floats[i];
477 }
478 return res;
479 }
480
481 @Test
482 @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
483 counts = {IRNode.VECTOR_CAST_D2F, IRNode.VECTOR_SIZE + "min(max_double, max_float)", ">0"})
484 public float[] convertDoubleToFloat() {
485 float[] res = new float[SIZE];
486 for (int i = 0; i < SIZE; i++) {
487 res[i] = (float) doubles[i];
488 }
489 return res;
490 }
491 }