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 } --- EOF ---