1 /*
2 * Copyright (c) 2025, Rivos Inc. All rights reserved.
3 * Copyright (c) 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 package compiler.c2.cmove;
26
27 import compiler.lib.ir_framework.*;
28 import java.util.Random;
29 import jdk.test.lib.Asserts;
30 import jdk.test.lib.Utils;
31
32 /*
33 * @test
34 * @key randomness
35 * @summary Test conditional move + compare object.
36 * @library /test/lib /
37 * @run driver ${test.main.class}
38 */
39
40 public class TestScalarConditionalMoveCmpObj {
41 final private static int SIZE = 1024;
42 private static final Random RANDOM = Utils.getRandomInstance();
43
44 public static void main(String[] args) {
45 TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov",
46 "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders", "-XX:+UseCompressedOops");
47 TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov",
48 "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders", "-XX:-UseCompressedOops");
49 TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov",
50 "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders", "-XX:+UseCompressedOops");
51 TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov",
52 "-XX:+UnlockExperimentalVMOptions", "-XX:+UseCompactObjectHeaders", "-XX:-UseCompressedOops");
53 }
54
55 // While a value of type Object can be a value object, a value of type NotValue
56 // cannot since it is a non-abstract non-value class and so cannot be the base
57 // class of a value class. This makes sure that comparisons between values of
58 // this type are simply pointer comparisons, and not substitutability
59 static class NotValue {}
60
61 // Object comparison
62 // O for I
63 private int cmoveOEQforI(Object a, Object b, int c, int d) {
64 return (a == b) ? c : d;
65 }
66
67 private int cmoveONEforI(Object a, Object b, int c, int d) {
68 return (a != b) ? c : d;
69 }
70
71 // O for L
72 private long cmoveOEQforL(Object a, Object b, long c, long d) {
73 return (a == b) ? c : d;
74 }
75
76 private long cmoveONEforL(Object a, Object b, long c, long d) {
77 return (a != b) ? c : d;
78 }
79
80 // O for F
81 private float cmoveOEQforF(Object a, Object b, float c, float d) {
82 return (a == b) ? c : d;
83 }
84
85 private float cmoveONEforF(Object a, Object b, float c, float d) {
86 return (a != b) ? c : d;
87 }
88
89 // O for D
90 private double cmoveOEQforD(Object a, Object b, double c, double d) {
91 return (a == b) ? c : d;
92 }
93
94 private double cmoveONEforD(Object a, Object b, double c, double d) {
95 return (a != b) ? c : d;
96 }
97
98 // Tests shows CMoveI is generated, so let @IR verify CMOVE_I.
99 //
100 @Test
101 @IR(failOn = {IRNode.STORE_VECTOR})
102 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_P, ">0"},
103 applyIf = {"UseCompressedOops", "false"})
104 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_N, ">0"},
105 applyIf = {"UseCompressedOops", "true"})
106 private static void testCMoveOEQforI(NotValue[] a, NotValue[] b, int[] c, int[] d, int[] r, int[] r2) {
107 for (int i = 0; i < a.length; i++) {
108 int cc = c[i];
109 int dd = d[i];
110 r2[i] = cc + dd;
111 r[i] = (a[i] == b[i]) ? cc : dd;
112 }
113 }
114
115 @Test
116 @IR(failOn = {IRNode.STORE_VECTOR})
117 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_P, ">0"},
118 applyIf = {"UseCompressedOops", "false"})
119 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_N, ">0"},
120 applyIf = {"UseCompressedOops", "true"})
121 private static void testCMoveONEforI(NotValue[] a, NotValue[] b, int[] c, int[] d, int[] r, int[] r2) {
122 for (int i = 0; i < a.length; i++) {
123 int cc = c[i];
124 int dd = d[i];
125 r2[i] = cc + dd;
126 r[i] = (a[i] != b[i]) ? cc : dd;
127 }
128 }
129
130 // So far, CMoveL is not guaranteed to be generated, so @IR not verify CMOVE_L.
131 // TODO: enable CMOVE_L verification when it's guaranteed to generate CMOVE_L.
132 //
133 @Test
134 @IR(failOn = {IRNode.STORE_VECTOR})
135 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_P, ">0"},
136 // applyIf = {"UseCompressedOops", "false"})
137 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_N, ">0"},
138 // applyIf = {"UseCompressedOops", "true"})
139 private static void testCMoveOEQforL(NotValue[] a, NotValue[] b, long[] c, long[] d, long[] r, long[] r2) {
140 for (int i = 0; i < a.length; i++) {
141 long cc = c[i];
142 long dd = d[i];
143 r2[i] = cc + dd;
144 r[i] = (a[i] == b[i]) ? cc : dd;
145 }
146 }
147
148 @Test
149 @IR(failOn = {IRNode.STORE_VECTOR})
150 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_P, ">0"},
151 // applyIf = {"UseCompressedOops", "false"})
152 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_N, ">0"},
153 // applyIf = {"UseCompressedOops", "true"})
154 private static void testCMoveONEforL(NotValue[] a, NotValue[] b, long[] c, long[] d, long[] r, long[] r2) {
155 for (int i = 0; i < a.length; i++) {
156 long cc = c[i];
157 long dd = d[i];
158 r2[i] = cc + dd;
159 r[i] = (a[i] != b[i]) ? cc : dd;
160 }
161 }
162
163 @Test
164 @IR(failOn = {IRNode.STORE_VECTOR})
165 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_P, ">0"},
166 applyIf = {"UseCompressedOops", "false"})
167 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_N, ">0"},
168 applyIf = {"UseCompressedOops", "true"})
169 private static void testCMoveOEQforF(NotValue[] a, NotValue[] b, float[] c, float[] d, float[] r, float[] r2) {
170 for (int i = 0; i < a.length; i++) {
171 float cc = c[i];
172 float dd = d[i];
173 r2[i] = cc + dd;
174 r[i] = (a[i] == b[i]) ? cc : dd;
175 }
176 }
177
178 @Test
179 @IR(failOn = {IRNode.STORE_VECTOR})
180 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_P, ">0"},
181 applyIf = {"UseCompressedOops", "false"})
182 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_N, ">0"},
183 applyIf = {"UseCompressedOops", "true"})
184 private static void testCMoveONEforF(NotValue[] a, NotValue[] b, float[] c, float[] d, float[] r, float[] r2) {
185 for (int i = 0; i < a.length; i++) {
186 float cc = c[i];
187 float dd = d[i];
188 r2[i] = cc + dd;
189 r[i] = (a[i] != b[i]) ? cc : dd;
190 }
191 }
192
193 @Test
194 @IR(failOn = {IRNode.STORE_VECTOR})
195 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_P, ">0"},
196 applyIf = {"UseCompressedOops", "false"})
197 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_N, ">0"},
198 applyIf = {"UseCompressedOops", "true"})
199 private static void testCMoveOEQforD(NotValue[] a, NotValue[] b, double[] c, double[] d, double[] r, double[] r2) {
200 for (int i = 0; i < a.length; i++) {
201 double cc = c[i];
202 double dd = d[i];
203 r2[i] = cc + dd;
204 r[i] = (a[i] == b[i]) ? cc : dd;
205 }
206 }
207
208 @Test
209 @IR(failOn = {IRNode.STORE_VECTOR})
210 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_P, ">0"},
211 applyIf = {"UseCompressedOops", "false"})
212 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_N, ">0"},
213 applyIf = {"UseCompressedOops", "true"})
214 private static void testCMoveONEforD(NotValue[] a, NotValue[] b, double[] c, double[] d, double[] r, double[] r2) {
215 for (int i = 0; i < a.length; i++) {
216 double cc = c[i];
217 double dd = d[i];
218 r2[i] = cc + dd;
219 r[i] = (a[i] != b[i]) ? cc : dd;
220 }
221 }
222
223 @Warmup(0)
224 @Run(test = {// Object
225 "testCMoveOEQforI",
226 "testCMoveONEforI",
227 "testCMoveOEQforL",
228 "testCMoveONEforL",
229 "testCMoveOEQforF",
230 "testCMoveONEforF",
231 "testCMoveOEQforD",
232 "testCMoveONEforD",
233 })
234 private void testCMove_runner_two() {
235 NotValue[] aO = new NotValue[SIZE];
236 NotValue[] bO = new NotValue[SIZE];
237 int[] cI = new int[SIZE];
238 int[] dI = new int[SIZE];
239 int[] rI = new int[SIZE];
240 long[] cL = new long[SIZE];
241 long[] dL = new long[SIZE];
242 long[] rL = new long[SIZE];
243 float[] cF = new float[SIZE];
244 float[] dF = new float[SIZE];
245 float[] rF = new float[SIZE];
246 double[] cD = new double[SIZE];
247 double[] dD = new double[SIZE];
248 double[] rD = new double[SIZE];
249
250 init(aO);
251 shuffle(aO, bO);
252 init(cI);
253 init(dI);
254 init(cL);
255 init(dL);
256 init(cF);
257 init(dF);
258 init(cD);
259 init(dD);
260
261 testCMoveOEQforI(aO, bO, cI, dI, rI, rI);
262 for (int i = 0; i < SIZE; i++) {
263 Asserts.assertEquals(rI[i], cmoveOEQforI(aO[i], bO[i], cI[i], dI[i]));
264 }
265
266 testCMoveONEforI(aO, bO, cI, dI, rI, rI);
267 for (int i = 0; i < SIZE; i++) {
268 Asserts.assertEquals(rI[i], cmoveONEforI(aO[i], bO[i], cI[i], dI[i]));
269 }
270
271 testCMoveOEQforL(aO, bO, cL, dL, rL, rL);
272 for (int i = 0; i < SIZE; i++) {
273 Asserts.assertEquals(rL[i], cmoveOEQforL(aO[i], bO[i], cL[i], dL[i]));
274 }
275
276 testCMoveONEforL(aO, bO, cL, dL, rL, rL);
277 for (int i = 0; i < SIZE; i++) {
278 Asserts.assertEquals(rL[i], cmoveONEforL(aO[i], bO[i], cL[i], dL[i]));
279 }
280
281 testCMoveOEQforF(aO, bO, cF, dF, rF, rF);
282 for (int i = 0; i < SIZE; i++) {
283 Asserts.assertEquals(rF[i], cmoveOEQforF(aO[i], bO[i], cF[i], dF[i]));
284 }
285
286 testCMoveONEforF(aO, bO, cF, dF, rF, rF);
287 for (int i = 0; i < SIZE; i++) {
288 Asserts.assertEquals(rF[i], cmoveONEforF(aO[i], bO[i], cF[i], dF[i]));
289 }
290
291 testCMoveOEQforD(aO, bO, cD, dD, rD, rD);
292 for (int i = 0; i < SIZE; i++) {
293 Asserts.assertEquals(rD[i], cmoveOEQforD(aO[i], bO[i], cD[i], dD[i]));
294 }
295
296 testCMoveONEforD(aO, bO, cD, dD, rD, rD);
297 for (int i = 0; i < SIZE; i++) {
298 Asserts.assertEquals(rD[i], cmoveONEforD(aO[i], bO[i], cD[i], dD[i]));
299 }
300
301 }
302
303 private static void init(NotValue[] a) {
304 for (int i = 0; i < SIZE; i++) {
305 a[i] = new NotValue();
306 }
307 }
308
309 private static void shuffle(NotValue[] a, NotValue[] b) {
310 for (int i = 0; i < a.length; i++) {
311 b[i] = a[i];
312 }
313 Random rand = Utils.getRandomInstance();
314 for (int i = 0; i < SIZE; i++) {
315 if (rand.nextInt(5) == 0) {
316 NotValue t = b[i];
317 b[i] = b[SIZE-1-i];
318 b[SIZE-1-i] = t;
319 }
320 }
321 }
322
323 private static void init(int[] a) {
324 for (int i = 0; i < SIZE; i++) {
325 a[i] = RANDOM.nextInt();
326 }
327 }
328
329 private static void init(long[] a) {
330 for (int i = 0; i < SIZE; i++) {
331 a[i] = RANDOM.nextLong();
332 }
333 }
334
335 private static void init(float[] a) {
336 for (int i = 0; i < SIZE; i++) {
337 a[i] = switch(RANDOM.nextInt() % 20) {
338 case 0 -> Float.NaN;
339 case 1 -> 0;
340 case 2 -> 1;
341 case 3 -> Float.POSITIVE_INFINITY;
342 case 4 -> Float.NEGATIVE_INFINITY;
343 case 5 -> Float.MAX_VALUE;
344 case 6 -> Float.MIN_VALUE;
345 case 7, 8, 9 -> RANDOM.nextFloat();
346 default -> Float.intBitsToFloat(RANDOM.nextInt());
347 };
348 }
349 }
350
351 private static void init(double[] a) {
352 for (int i = 0; i < SIZE; i++) {
353 a[i] = switch(RANDOM.nextInt() % 20) {
354 case 0 -> Double.NaN;
355 case 1 -> 0;
356 case 2 -> 1;
357 case 3 -> Double.POSITIVE_INFINITY;
358 case 4 -> Double.NEGATIVE_INFINITY;
359 case 5 -> Double.MAX_VALUE;
360 case 6 -> Double.MIN_VALUE;
361 case 7, 8, 9 -> RANDOM.nextDouble();
362 default -> Double.longBitsToDouble(RANDOM.nextLong());
363 };
364 }
365 }
366 }