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