1 /*
2 * Copyright (c) 2025, Rivos Inc. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24 package compiler.c2.irTests;
25
26 import compiler.lib.ir_framework.*;
27 import java.util.Random;
28 import jdk.test.lib.Asserts;
29 import jdk.test.lib.Utils;
30
31 /*
32 * @test
33 * @summary Test conditional move + compare object.
34 * @library /test/lib /
35 * @run driver ${test.main.class}
36 */
37
38 public class TestScalarConditionalMoveCmpObj {
39 final private static int SIZE = 1024;
40 private static final Random RANDOM = Utils.getRandomInstance();
41
42 public static void main(String[] args) {
43 TestFramework.runWithFlags("-XX:+UseCMoveUnconditionally", "-XX:-UseVectorCmov",
44 "-XX:+UnlockExperimentalVMOptions", "-XX:-UseCompactObjectHeaders", "-XX:+UseCompressedOops");
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 }
52
53 // Object comparison
54 // O for I
55 private int cmoveOEQforI(Object a, Object b, int c, int d) {
56 return (a == b) ? c : d;
57 }
58
59 private int cmoveONEforI(Object a, Object b, int c, int d) {
60 return (a != b) ? c : d;
61 }
62
63 // O for L
64 private long cmoveOEQforL(Object a, Object b, long c, long d) {
65 return (a == b) ? c : d;
66 }
67
68 private long cmoveONEforL(Object a, Object b, long c, long d) {
69 return (a != b) ? c : d;
70 }
71
72 // O for F
73 private float cmoveOEQforF(Object a, Object b, float c, float d) {
74 return (a == b) ? c : d;
75 }
76
77 private float cmoveONEforF(Object a, Object b, float c, float d) {
78 return (a != b) ? c : d;
79 }
80
81 // O for D
82 private double cmoveOEQforD(Object a, Object b, double c, double d) {
83 return (a == b) ? c : d;
84 }
85
86 private double cmoveONEforD(Object a, Object b, double c, double d) {
87 return (a != b) ? c : d;
88 }
89
90 // Tests shows CMoveI is generated, so let @IR verify CMOVE_I.
91 //
92 @Test
93 @IR(failOn = {IRNode.STORE_VECTOR})
94 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_P, ">0"},
95 applyIf = {"UseCompressedOops", "false"})
96 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_N, ">0"},
97 applyIf = {"UseCompressedOops", "true"})
98 private static void testCMoveOEQforI(Object[] a, Object[] b, int[] c, int[] d, int[] r, int[] r2) {
99 for (int i = 0; i < a.length; i++) {
100 int cc = c[i];
101 int dd = d[i];
102 r2[i] = cc + dd;
103 r[i] = (a[i] == b[i]) ? cc : dd;
104 }
105 }
106
107 @Test
108 @IR(failOn = {IRNode.STORE_VECTOR})
109 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_P, ">0"},
110 applyIf = {"UseCompressedOops", "false"})
111 @IR(counts = {IRNode.CMOVE_I, ">0", IRNode.CMP_N, ">0"},
112 applyIf = {"UseCompressedOops", "true"})
113 private static void testCMoveONEforI(Object[] a, Object[] b, int[] c, int[] d, int[] r, int[] r2) {
114 for (int i = 0; i < a.length; i++) {
115 int cc = c[i];
116 int dd = d[i];
117 r2[i] = cc + dd;
118 r[i] = (a[i] != b[i]) ? cc : dd;
119 }
120 }
121
122 // So far, CMoveL is not guaranteed to be generated, so @IR not verify CMOVE_L.
123 // TODO: enable CMOVE_L verification when it's guaranteed to generate CMOVE_L.
124 //
125 @Test
126 @IR(failOn = {IRNode.STORE_VECTOR})
127 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_P, ">0"},
128 // applyIf = {"UseCompressedOops", "false"})
129 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_N, ">0"},
130 // applyIf = {"UseCompressedOops", "true"})
131 private static void testCMoveOEQforL(Object[] a, Object[] b, long[] c, long[] d, long[] r, long[] r2) {
132 for (int i = 0; i < a.length; i++) {
133 long cc = c[i];
134 long dd = d[i];
135 r2[i] = cc + dd;
136 r[i] = (a[i] == b[i]) ? cc : dd;
137 }
138 }
139
140 @Test
141 @IR(failOn = {IRNode.STORE_VECTOR})
142 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_P, ">0"},
143 // applyIf = {"UseCompressedOops", "false"})
144 // @IR(counts = {IRNode.CMOVE_L, ">0", IRNode.CMP_N, ">0"},
145 // applyIf = {"UseCompressedOops", "true"})
146 private static void testCMoveONEforL(Object[] a, Object[] b, long[] c, long[] d, long[] r, long[] r2) {
147 for (int i = 0; i < a.length; i++) {
148 long cc = c[i];
149 long dd = d[i];
150 r2[i] = cc + dd;
151 r[i] = (a[i] != b[i]) ? cc : dd;
152 }
153 }
154
155 @Test
156 @IR(failOn = {IRNode.STORE_VECTOR})
157 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_P, ">0"},
158 applyIf = {"UseCompressedOops", "false"})
159 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_N, ">0"},
160 applyIf = {"UseCompressedOops", "true"})
161 private static void testCMoveOEQforF(Object[] a, Object[] b, float[] c, float[] d, float[] r, float[] r2) {
162 for (int i = 0; i < a.length; i++) {
163 float cc = c[i];
164 float dd = d[i];
165 r2[i] = cc + dd;
166 r[i] = (a[i] == b[i]) ? cc : dd;
167 }
168 }
169
170 @Test
171 @IR(failOn = {IRNode.STORE_VECTOR})
172 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_P, ">0"},
173 applyIf = {"UseCompressedOops", "false"})
174 @IR(counts = {IRNode.CMOVE_F, ">0", IRNode.CMP_N, ">0"},
175 applyIf = {"UseCompressedOops", "true"})
176 private static void testCMoveONEforF(Object[] a, Object[] b, float[] c, float[] d, float[] r, float[] r2) {
177 for (int i = 0; i < a.length; i++) {
178 float cc = c[i];
179 float dd = d[i];
180 r2[i] = cc + dd;
181 r[i] = (a[i] != b[i]) ? cc : dd;
182 }
183 }
184
185 @Test
186 @IR(failOn = {IRNode.STORE_VECTOR})
187 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_P, ">0"},
188 applyIf = {"UseCompressedOops", "false"})
189 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_N, ">0"},
190 applyIf = {"UseCompressedOops", "true"})
191 private static void testCMoveOEQforD(Object[] a, Object[] b, double[] c, double[] d, double[] r, double[] r2) {
192 for (int i = 0; i < a.length; i++) {
193 double cc = c[i];
194 double dd = d[i];
195 r2[i] = cc + dd;
196 r[i] = (a[i] == b[i]) ? cc : dd;
197 }
198 }
199
200 @Test
201 @IR(failOn = {IRNode.STORE_VECTOR})
202 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_P, ">0"},
203 applyIf = {"UseCompressedOops", "false"})
204 @IR(counts = {IRNode.CMOVE_D, ">0", IRNode.CMP_N, ">0"},
205 applyIf = {"UseCompressedOops", "true"})
206 private static void testCMoveONEforD(Object[] a, Object[] b, double[] c, double[] d, double[] r, double[] r2) {
207 for (int i = 0; i < a.length; i++) {
208 double cc = c[i];
209 double dd = d[i];
210 r2[i] = cc + dd;
211 r[i] = (a[i] != b[i]) ? cc : dd;
212 }
213 }
214
215 @Warmup(0)
216 @Run(test = {// Object
217 "testCMoveOEQforI",
218 "testCMoveONEforI",
219 "testCMoveOEQforL",
220 "testCMoveONEforL",
221 "testCMoveOEQforF",
222 "testCMoveONEforF",
223 "testCMoveOEQforD",
224 "testCMoveONEforD",
225 })
226 private void testCMove_runner_two() {
227 Object[] aO = new Object[SIZE];
228 Object[] bO = new Object[SIZE];
229 int[] cI = new int[SIZE];
230 int[] dI = new int[SIZE];
231 int[] rI = new int[SIZE];
232 long[] cL = new long[SIZE];
233 long[] dL = new long[SIZE];
234 long[] rL = new long[SIZE];
235 float[] cF = new float[SIZE];
236 float[] dF = new float[SIZE];
237 float[] rF = new float[SIZE];
238 double[] cD = new double[SIZE];
239 double[] dD = new double[SIZE];
240 double[] rD = new double[SIZE];
241
242 init(aO);
243 shuffle(aO, bO);
244 init(cL);
245 init(dL);
246 init(cF);
247 init(dF);
248 init(cD);
249 init(dD);
250
251 testCMoveOEQforI(aO, bO, cI, dI, rI, rI);
252 for (int i = 0; i < SIZE; i++) {
253 Asserts.assertEquals(rI[i], cmoveOEQforI(aO[i], bO[i], cI[i], dI[i]));
254 }
255
256 testCMoveONEforI(aO, bO, cI, dI, rI, rI);
257 for (int i = 0; i < SIZE; i++) {
258 Asserts.assertEquals(rI[i], cmoveONEforI(aO[i], bO[i], cI[i], dI[i]));
259 }
260
261 testCMoveOEQforL(aO, bO, cL, dL, rL, rL);
262 for (int i = 0; i < SIZE; i++) {
263 Asserts.assertEquals(rL[i], cmoveOEQforL(aO[i], bO[i], cL[i], dL[i]));
264 }
265
266 testCMoveONEforL(aO, bO, cL, dL, rL, rL);
267 for (int i = 0; i < SIZE; i++) {
268 Asserts.assertEquals(rL[i], cmoveONEforL(aO[i], bO[i], cL[i], dL[i]));
269 }
270
271 testCMoveOEQforF(aO, bO, cF, dF, rF, rF);
272 for (int i = 0; i < SIZE; i++) {
273 Asserts.assertEquals(rF[i], cmoveOEQforF(aO[i], bO[i], cF[i], dF[i]));
274 }
275
276 testCMoveONEforF(aO, bO, cF, dF, rF, rF);
277 for (int i = 0; i < SIZE; i++) {
278 Asserts.assertEquals(rF[i], cmoveONEforF(aO[i], bO[i], cF[i], dF[i]));
279 }
280
281 testCMoveOEQforD(aO, bO, cD, dD, rD, rD);
282 for (int i = 0; i < SIZE; i++) {
283 Asserts.assertEquals(rD[i], cmoveOEQforD(aO[i], bO[i], cD[i], dD[i]));
284 }
285
286 testCMoveONEforD(aO, bO, cD, dD, rD, rD);
287 for (int i = 0; i < SIZE; i++) {
288 Asserts.assertEquals(rD[i], cmoveONEforD(aO[i], bO[i], cD[i], dD[i]));
289 }
290
291 }
292
293 private static void init(Object[] a) {
294 for (int i = 0; i < SIZE; i++) {
295 a[i] = new Object();
296 }
297 }
298
299 private static void shuffle(Object[] a, Object[] b) {
300 for (int i = 0; i < a.length; i++) {
301 b[i] = a[i];
302 }
303 Random rand = new Random();
304 for (int i = 0; i < SIZE; i++) {
305 if (rand.nextInt(5) == 0) {
306 Object t = b[i];
307 b[i] = b[SIZE-1-i];
308 b[SIZE-1-i] = t;
309 }
310 }
311 }
312
313 private static void init(int[] a) {
314 for (int i = 0; i < SIZE; i++) {
315 a[i] = RANDOM.nextInt();
316 }
317 }
318
319 private static void init(long[] a) {
320 for (int i = 0; i < SIZE; i++) {
321 a[i] = RANDOM.nextLong();
322 }
323 }
324
325 private static void init(float[] a) {
326 for (int i = 0; i < SIZE; i++) {
327 a[i] = switch(RANDOM.nextInt() % 20) {
328 case 0 -> Float.NaN;
329 case 1 -> 0;
330 case 2 -> 1;
331 case 3 -> Float.POSITIVE_INFINITY;
332 case 4 -> Float.NEGATIVE_INFINITY;
333 case 5 -> Float.MAX_VALUE;
334 case 6 -> Float.MIN_VALUE;
335 case 7, 8, 9 -> RANDOM.nextFloat();
336 default -> Float.intBitsToFloat(RANDOM.nextInt());
337 };
338 }
339 }
340
341 private static void init(double[] a) {
342 for (int i = 0; i < SIZE; i++) {
343 a[i] = switch(RANDOM.nextInt() % 20) {
344 case 0 -> Double.NaN;
345 case 1 -> 0;
346 case 2 -> 1;
347 case 3 -> Double.POSITIVE_INFINITY;
348 case 4 -> Double.NEGATIVE_INFINITY;
349 case 5 -> Double.MAX_VALUE;
350 case 6 -> Double.MIN_VALUE;
351 case 7, 8, 9 -> RANDOM.nextDouble();
352 default -> Double.longBitsToDouble(RANDOM.nextLong());
353 };
354 }
355 }
356 }