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 // It should, however, not affect 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         counts = {IRNode.VECTOR_CAST_S2F, IRNode.VECTOR_SIZE + "min(max_short, max_float)", ">0"})
258     public float[] convertShortToFloat() {
259         float[] res = new float[SIZE];
260         for (int i = 0; i < SIZE; i++) {
261             res[i] = (float) shorts[i];
262         }
263         return res;
264     }
265 
266     @Test
267     @IR(applyIfCPUFeature = {"rvv", "true"},
268         applyIf = {"MaxVectorSize", ">=32"},
269         counts = {IRNode.VECTOR_CAST_S2D, IRNode.VECTOR_SIZE + "min(max_short, max_double)", ">0"})
270     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true"},
271         applyIf = {"MaxVectorSize", ">=16"},
272         counts = {IRNode.VECTOR_CAST_S2D, IRNode.VECTOR_SIZE + "min(max_short, max_double)", ">0"})
273     public double[] convertShortToDouble() {
274         double[] res = new double[SIZE];
275         for (int i = 0; i < SIZE; i++) {
276             res[i] = (double) shorts[i];
277         }
278         return res;
279     }
280 
281     @Test
282     @IR(failOn = {IRNode.STORE_VECTOR})
283     // Subword vector casts with char do not work currently, see JDK-8349562.
284     // Assert the vectorization failure so that we are reminded to update
285     // the test when this limitation is addressed in the future.
286     public float[] convertCharToFloat() {
287         float[] res = new float[SIZE];
288         for (int i = 0; i < SIZE; i++) {
289             res[i] = (float) chars[i];
290         }
291         return res;
292     }
293 
294     @Test
295     @IR(failOn = {IRNode.STORE_VECTOR})
296     // Subword vector casts with char do not work currently, see JDK-8349562.
297     // Assert the vectorization failure so that we are reminded to update
298     // the test when this limitation is addressed in the future.
299     public double[] convertCharToDouble() {
300         double[] res = new double[SIZE];
301         for (int i = 0; i < SIZE; i++) {
302             res[i] = (double) chars[i];
303         }
304         return res;
305     }
306 
307     // ---------------- Convert F/D to I/L ----------------
308     @Test
309     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "avx10_2", "true", "rvv", "true"},
310         counts = {IRNode.VECTOR_CAST_F2I, IRNode.VECTOR_SIZE + "min(max_float, max_int)", "> 0"})
311     @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
312         applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
313     @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
314         applyIfCPUFeature = {"avx10_2", "true"})
315     public int[] convertFloatToInt() {
316         int[] res = new int[SIZE];
317         for (int i = 0; i < SIZE; i++) {
318             res[i] = (int) floats[i];
319         }
320         return res;
321     }
322 
323     @Test
324     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx512dq", "true", "avx10_2", "true", "rvv", "true"},
325         counts = {IRNode.VECTOR_CAST_F2L, IRNode.VECTOR_SIZE + "min(max_float, max_long)", "> 0"})
326     @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
327         applyIfCPUFeatureAnd = {"avx512dq", "true", "avx10_2", "false"})
328     @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
329         applyIfCPUFeature = {"avx10_2", "true"})
330     public long[] convertFloatToLong() {
331         long[] res = new long[SIZE];
332         for (int i = 0; i < SIZE; i++) {
333             res[i] = (long) floats[i];
334         }
335         return res;
336     }
337 
338     @Test
339     @IR(applyIfCPUFeatureOr = {"sve", "true", "avx", "true", "avx10_2", "true", "rvv", "true"},
340         counts = {IRNode.VECTOR_CAST_D2I, IRNode.VECTOR_SIZE + "min(max_double, max_int)", "> 0"})
341     @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
342         applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
343     @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
344         applyIfCPUFeature = {"avx10_2", "true"})
345     public int[] convertDoubleToInt() {
346         int[] res = new int[SIZE];
347         for (int i = 0; i < SIZE; i++) {
348             res[i] = (int) doubles[i];
349         }
350         return res;
351     }
352 
353     @Test
354     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx512dq", "true", "avx10_2", "true", "rvv", "true"},
355         counts = {IRNode.VECTOR_CAST_D2L, IRNode.VECTOR_SIZE + "min(max_double, max_long)", "> 0"})
356     @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
357         applyIfCPUFeatureAnd = {"avx512dq", "true", "avx10_2", "false"})
358     @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
359         applyIfCPUFeature = {"avx10_2", "true"})
360     public long[] convertDoubleToLong() {
361         long[] res = new long[SIZE];
362         for (int i = 0; i < SIZE; i++) {
363             res[i] = (long) doubles[i];
364         }
365         return res;
366     }
367 
368     // ---------------- Convert F/D to Subword-I ----------------
369     @Test
370     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx2", "true", "avx10_2", "true", "rvv", "true"},
371         applyIf = {"AlignVector", "false"},
372         counts = {IRNode.VECTOR_CAST_F2S, IRNode.VECTOR_SIZE + "min(max_float, max_short)", "> 0"})
373     @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
374         applyIf = {"AlignVector", "false"},
375         applyIfCPUFeatureAnd = {"avx2", "true", "avx10_2", "false"})
376     @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
377         applyIf = {"AlignVector", "false"},
378         applyIfCPUFeature = {"avx10_2", "true"})
379     public short[] convertFloatToShort() {
380         short[] res = new short[SIZE];
381         for (int i = 0; i < SIZE; i++) {
382             res[i] = (short) floats[i];
383         }
384         return res;
385     }
386 
387     @Test
388     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx2", "true", "avx10_2", "true", "rvv", "true"},
389         applyIf = {"AlignVector", "false"},
390         counts = {IRNode.VECTOR_CAST_F2S, IRNode.VECTOR_SIZE + "min(max_float, max_char)", "> 0"})
391     @IR(counts = {IRNode.X86_VCAST_F2X, "> 0"},
392         applyIf = {"AlignVector", "false"},
393         applyIfCPUFeatureAnd = {"avx2", "true", "avx10_2", "false"})
394     @IR(counts = {IRNode.X86_VCAST_F2X_AVX10_2, "> 0"},
395         applyIf = {"AlignVector", "false"},
396         applyIfCPUFeature = {"avx10_2", "true"})
397     public char[] convertFloatToChar() {
398         char[] res = new char[SIZE];
399         for (int i = 0; i < SIZE; i++) {
400             res[i] = (char) floats[i];
401         }
402         return res;
403     }
404 
405     @Test
406     @IR(applyIfCPUFeature = {"rvv", "true"},
407         applyIf = {"MaxVectorSize", ">=32"},
408         counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_short)", "> 0"})
409     @IR(applyIfCPUFeatureOr = {"sve", "true", "avx", "true", "avx10_2", "true"},
410         applyIf = {"MaxVectorSize", ">=16"},
411         counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_short)", "> 0"})
412     @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
413         applyIf = {"MaxVectorSize", ">=16"},
414         applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
415     @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
416         applyIf = {"MaxVectorSize", ">=16"},
417         applyIfCPUFeature = {"avx10_2", "true"})
418     public short[] convertDoubleToShort() {
419         short[] res = new short[SIZE];
420         for (int i = 0; i < SIZE; i++) {
421             res[i] = (short) doubles[i];
422         }
423         return res;
424     }
425 
426     @Test
427     @IR(applyIfCPUFeature = {"rvv", "true"},
428         applyIf = {"MaxVectorSize", ">= 32"},
429         counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_char)", "> 0"})
430     @IR(applyIfCPUFeatureOr = {"sve", "true", "avx", "true", "avx10_2", "true"},
431         applyIf = {"MaxVectorSize", ">= 16"},
432         counts = {IRNode.VECTOR_CAST_D2S, IRNode.VECTOR_SIZE + "min(max_double, max_char)", "> 0"})
433     @IR(counts = {IRNode.X86_VCAST_D2X, "> 0"},
434         applyIf = {"MaxVectorSize", ">=16"},
435         applyIfCPUFeatureAnd = {"avx", "true", "avx10_2", "false"})
436     @IR(counts = {IRNode.X86_VCAST_D2X_AVX10_2, "> 0"},
437         applyIf = {"MaxVectorSize", ">=16"},
438         applyIfCPUFeature = {"avx10_2", "true"})
439     public char[] convertDoubleToChar() {
440         char[] res = new char[SIZE];
441         for (int i = 0; i < SIZE; i++) {
442             res[i] = (char) doubles[i];
443         }
444         return res;
445     }
446 
447     // ---------------- Convert Between F & D ----------------
448     @Test
449     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
450         counts = {IRNode.VECTOR_CAST_F2D, IRNode.VECTOR_SIZE + "min(max_float, max_double)", ">0"})
451     public double[] convertFloatToDouble() {
452         double[] res = new double[SIZE];
453         for (int i = 0; i < SIZE; i++) {
454             res[i] = (double) floats[i];
455         }
456         return res;
457     }
458 
459     @Test
460     @IR(applyIfCPUFeatureOr = {"asimd", "true", "avx", "true", "rvv", "true"},
461         counts = {IRNode.VECTOR_CAST_D2F, IRNode.VECTOR_SIZE + "min(max_double, max_float)", ">0"})
462     public float[] convertDoubleToFloat() {
463         float[] res = new float[SIZE];
464         for (int i = 0; i < SIZE; i++) {
465             res[i] = (float) doubles[i];
466         }
467         return res;
468     }
469 }