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