1 /* 2 * Copyright (c) 2025, Oracle and/or its affiliates. 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 /* 25 * @test id=GetAndSet 26 * @bug 8020282 27 * @summary Test that we do not generate redundant leas on x86 for AtomicReference.getAndSet. 28 * @requires os.simpleArch == "x64" & vm.opt.TieredCompilation != false 29 * @modules jdk.compiler/com.sun.tools.javac.util 30 * @library /test/lib / 31 * @run driver compiler.codegen.TestRedundantLea GetAndSet 32 */ 33 34 /* 35 * @test id=StringEquals 36 * @bug 8020282 37 * @summary Test that we do not generate redundant leas on x86 for String.Equals. 38 * @requires os.simpleArch == "x64" & vm.opt.TieredCompilation != false 39 * @modules jdk.compiler/com.sun.tools.javac.util 40 * @library /test/lib / 41 * @run driver compiler.codegen.TestRedundantLea StringEquals 42 */ 43 44 /* 45 * @test id=StringInflate 46 * @bug 8020282 47 * @summary Test that we do not generate redundant leas on x86 for StringConcat intrinsics. 48 * @requires os.simpleArch == "x64" & vm.opt.TieredCompilation != false 49 * @modules jdk.compiler/com.sun.tools.javac.util 50 * @library /test/lib / 51 * @run driver compiler.codegen.TestRedundantLea StringInflate 52 */ 53 54 /* 55 * @test id=RegexFind 56 * @bug 8020282 57 * @summary Test that we do not generate redundant leas on x86 when performing regex matching. 58 * @requires os.simpleArch == "x64" & vm.opt.TieredCompilation != false & vm.opt.UseAvx == 3 59 * @modules jdk.compiler/com.sun.tools.javac.util 60 * @library /test/lib / 61 * @run driver compiler.codegen.TestRedundantLea RegexFind 62 */ 63 64 /* 65 * @test id=StoreNSerial 66 * @bug 8020282 67 * @summary Test that we do not generate redundant leas on x86 when storing narrow oops to object arrays. 68 * @requires os.simpleArch == "x64" & vm.gc.Serial 69 * @modules jdk.compiler/com.sun.tools.javac.util 70 * @library /test/lib / 71 * @run driver compiler.codegen.TestRedundantLea StoreNSerial 72 */ 73 74 /* 75 * @test id=StoreNParallel 76 * @bug 8020282 77 * @summary Test that we do not generate redundant leas on x86 when storing narrow oops to object arrays. 78 * @requires os.simpleArch == "x64" & vm.gc.Parallel 79 * @modules jdk.compiler/com.sun.tools.javac.util 80 * @library /test/lib / 81 * @run driver compiler.codegen.TestRedundantLea StoreNParallel 82 */ 83 84 /* 85 * @test id=Spill 86 * @bug 8020282 87 * @summary Test that we do not generate redundant leas and remove related spills on x86. 88 * @requires os.simpleArch == "x64" 89 * @modules jdk.compiler/com.sun.tools.javac.util 90 * @library /test/lib / 91 * @run driver compiler.codegen.TestRedundantLea Spill 92 */ 93 94 95 package compiler.codegen; 96 97 import java.util.concurrent.atomic.*; 98 import java.util.regex.Matcher; 99 import java.util.regex.Pattern; 100 101 import com.sun.tools.javac.util.*; 102 103 import compiler.lib.ir_framework.*; 104 105 // The following tests ensure that we do not generate a redundant lea instruction on x86. 106 // These get generated on chained dereferences for the rules leaPCompressedOopOffset, 107 // leaP8Narrow, and leaP32Narrow and stem from a decodeHeapOopNotNull that is not needed 108 // unless the derived oop is added to an oop map. The redundant lea is removed with an 109 // opto assembly peephole optimization. Hence, all tests below feature a negative test 110 // run with -XX:-OptoPeephole to detect changes that obsolete that peephole. 111 // Further, all tests are run with different max heap sizes to trigger the generation of 112 // different lea match rules: -XX:MaxHeapSize=32m generates leaP(8|32)Narrow and 113 // -XX:MaxHeapSize=4g generates leaPCompressedOopOffset, since the address computation 114 // needs to shift left by 3. 115 public class TestRedundantLea { 116 public static void main(String[] args) { 117 String testName = args[0]; 118 TestFramework framework; 119 switch (testName) { 120 case "GetAndSet" -> { 121 framework = new TestFramework(GetAndSetTest.class); 122 } 123 case "StringEquals" -> { 124 framework = new TestFramework(StringEqualsTest.class); 125 framework.addHelperClasses(StringEqualsTestHelper.class); 126 } 127 case "StringInflate" -> { 128 framework = new TestFramework(StringInflateTest.class); 129 framework.addFlags("--add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED"); 130 } 131 case "RegexFind" -> { 132 framework = new TestFramework(RegexFindTest.class); 133 } 134 case "StoreNSerial" -> { 135 framework = new TestFramework(StoreNTest.class); 136 framework.addFlags("-XX:+UseSerialGC"); 137 } 138 case "StoreNParallel" -> { 139 framework = new TestFramework(StoreNTest.class); 140 framework.addFlags("-XX:+UseParallelGC"); 141 } 142 case "Spill" -> { 143 framework = new TestFramework(SpillTest.class); 144 } 145 default -> { 146 throw new IllegalArgumentException("Unknown test name \"" + testName +"\""); 147 } 148 } 149 150 Scenario[] scenarios = new Scenario[2]; 151 // Scenario for the negative test without peephole optimizations. 152 scenarios[0] = new Scenario(0, "-XX:+IgnoreUnrecognizedVMOptions", "-XX:-OptoPeephole"); 153 // Scenario for the positive test with +OptoPeephole (the default on x64). 154 scenarios[1] = new Scenario(1); 155 framework.addScenarios(scenarios).start(); 156 } 157 } 158 159 // This generates a leaP* rule for the chained dereference of obj.value that 160 // gets passed to the get and set VM intrinsic. 161 class GetAndSetTest { 162 private static final Object CURRENT = new Object(); 163 private final AtomicReference<Object> obj = new AtomicReference<Object>(); 164 165 @Test 166 @IR(counts = {IRNode.LEA_P, "=1"}, 167 phase = {CompilePhase.FINAL_CODE}, 168 applyIfPlatform = {"mac", "false"}) 169 // Negative test 170 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "=1"}, 171 phase = {CompilePhase.FINAL_CODE}, 172 applyIf = {"OptoPeephole", "false"}) 173 // Test that the peephole worked for leaP(8|32)Narrow 174 @IR(failOn = {IRNode.DECODE_HEAP_OOP_NOT_NULL}, 175 phase = {CompilePhase.FINAL_CODE}, 176 applyIf = {"OptoPeephole", "true"}) 177 public void testGetAndSet() { 178 obj.getAndSet(CURRENT); 179 } 180 } 181 182 // This generates leaP* rules for the chained dereferences of the String.value 183 // fields that are used in the string_equals VM intrinsic. 184 class StringEqualsTest { 185 final StringEqualsTestHelper strEqHelper = new StringEqualsTestHelper("I am the string that is tested against"); 186 187 @Setup 188 private static Object[] setup() { 189 return new Object[]{"I will be compared!"}; 190 } 191 192 @Test 193 @IR(counts = {IRNode.LEA_P, "=2"}, 194 phase = {CompilePhase.FINAL_CODE}, 195 applyIfPlatform = {"mac", "false"}) 196 // Negative test 197 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "=3"}, 198 phase = {CompilePhase.FINAL_CODE}, 199 applyIf = {"OptoPeephole", "false"}) 200 // Test that the peephole worked for leaPCompressedOopOffset 201 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "=1"}, 202 phase = {CompilePhase.FINAL_CODE}, 203 applyIf = {"OptoPeephole", "true"}) 204 @Arguments(setup = "setup") 205 public boolean test(String str) { 206 return strEqHelper.doEquals(str); 207 } 208 } 209 210 class StringEqualsTestHelper { 211 private String str; 212 213 public StringEqualsTestHelper(String str) { 214 this.str = str; 215 } 216 217 @ForceInline 218 public boolean doEquals(String other) { 219 return this.str.equals(other); 220 } 221 } 222 223 // With all VM instrinsics disabled, this test only generates a leaP* rule 224 // before the string_inflate intrinsic (with -XX:-OptimizeStringConcat no 225 // leaP* rule is generated). With VM intrinsics enabled (this is the case 226 // here) leaP* rules are also generated for the string_equals and arrays_hashcode 227 // VM instrinsics. 228 // This generates a larger number of decodes for -XX:UseAVX={0,1} than for 229 // other flags. 230 class StringInflateTest { 231 @Setup 232 private static Object[] setup() { 233 Names names = new Names(new Context()); 234 Name n1 = names.fromString("one"); 235 Name n2 = names.fromString("two"); 236 return new Object[] {n1, n2}; 237 } 238 239 @Test 240 // TODO: Make tests more precise 241 @IR(counts = {IRNode.LEA_P, "=2"}, 242 phase = {CompilePhase.FINAL_CODE}, 243 applyIfPlatform = {"mac", "false"}) 244 // Negative 245 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, ">=5"}, 246 phase = {CompilePhase.FINAL_CODE}, 247 applyIf = {"OptoPeephole", "false"}) 248 // 2 decodes get removed 249 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, ">=3"}, 250 phase = {CompilePhase.FINAL_CODE}, 251 applyIf = {"OptoPeephole", "true"}) 252 @Arguments(setup = "setup") 253 public static Name test(Name n1, Name n2) { 254 return n1.append(n2); 255 } 256 } 257 258 // This test case generates leaP* rules before arrayof_jint_fill intrinsics, 259 // but only with -XX:+UseAVX3. 260 class RegexFindTest { 261 @Setup 262 private static Object[] setup() { 263 Pattern pat = Pattern.compile("27"); 264 Matcher m = pat.matcher(" 274 leaPCompressedOopOffset === _ 275 277 [[ 2246 165 294 ]] #16/0x0000000000000010byte[int:>=0]"); 265 return new Object[] { m }; 266 } 267 268 @Test 269 // TODO: Make tests more precise 270 @IR(counts = {IRNode.LEA_P, "=1"}, 271 phase = {CompilePhase.FINAL_CODE}, 272 applyIfPlatform = {"mac", "false"}) 273 // Due to unpredictable code generation, we cannot match the exact number of decodes below. 274 // Negative test 275 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, ">=7"}, 276 phase = {CompilePhase.FINAL_CODE}, 277 applyIfAnd = {"OptoPeephole", "false", "UseAVX", "=3"}) 278 // Test that the peephole worked for leaPCompressedOopOffset 279 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, ">=6"}, 280 phase = {CompilePhase.FINAL_CODE}, 281 applyIfAnd = {"OptoPeephole", "true", "UseAVX", "=3"}) 282 @Arguments(setup = "setup") 283 public boolean test(Matcher m) { 284 return m.find(); 285 } 286 } 287 288 // The matcher generates leaP* rules for storing an object in an array of objects 289 // at a constant offset, but only when using the Serial or Parallel GC. 290 // Here, we can also manipulate the offset such that we get a leaP32Narrow rule. 291 class StoreNTest { 292 private static final int SOME_SIZE = 42; 293 private static final int OFFSET8BIT_IDX = 3; 294 private static final int OFFSET32BIT_IDX = 33; 295 296 private static final Object CURRENT = new Object(); 297 private static final Object OTHER = new Object(); 298 299 private StoreNTestHelper[] classArr8bit = new StoreNTestHelper[SOME_SIZE]; 300 private StoreNTestHelper[] classArr32bit = new StoreNTestHelper[SOME_SIZE]; 301 302 @Test 303 @IR(counts = {IRNode.LEA_P, "=2"}, 304 phase = {CompilePhase.FINAL_CODE}, 305 applyIfPlatform = {"mac", "false"}) 306 // Negative test 307 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "=2"}, 308 phase = {CompilePhase.FINAL_CODE}, 309 applyIf = {"OptoPeephole", "false"}) 310 // Test that the peephole worked for leaPCompressedOopOffset 311 @IR(failOn = {IRNode.DECODE_HEAP_OOP_NOT_NULL}, 312 phase = {CompilePhase.FINAL_CODE}, 313 applyIf = {"OptoPeephole", "true"}) 314 public void testRemoveSpill() { 315 this.classArr8bit[OFFSET8BIT_IDX] = new StoreNTestHelper(CURRENT, OTHER); 316 this.classArr32bit[OFFSET32BIT_IDX] = new StoreNTestHelper(OTHER, CURRENT); 317 } 318 319 // This variation of the test above generates a split spill register path. 320 // Due to the complicated graph structure with the phis, the peephole 321 // cannot remove the redundant decode shared by both leaP*s. 322 @Test 323 @IR(counts = {IRNode.LEA_P, "=2"}, 324 phase = {CompilePhase.FINAL_CODE}, 325 applyIfPlatform = {"mac", "false"}) 326 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "=1"}, 327 phase = {CompilePhase.FINAL_CODE}, 328 applyIf = {"OptoPeephole", "false"}) 329 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "=1"}, 330 phase = {CompilePhase.FINAL_CODE}, 331 applyIf = {"OptoPeephole", "true"}) 332 public void testPhiSpill() { 333 this.classArr8bit[OFFSET8BIT_IDX] = new StoreNTestHelper(CURRENT, OTHER); 334 this.classArr8bit[OFFSET32BIT_IDX] = new StoreNTestHelper(CURRENT, OTHER); 335 } 336 } 337 338 class StoreNTestHelper { 339 Object o1; 340 Object o2; 341 342 public StoreNTestHelper(Object o1, Object o2) { 343 this.o1 = o1; 344 this.o2 = o2; 345 } 346 } 347 348 // This test validates that the peephole removes simple spills. 349 // The code for the test originates from compiler/escapeAnalysis/Test6775880.java. 350 class SpillTest { 351 int cnt; 352 int b[]; 353 String s; 354 355 @Run(test = "test") 356 public static void run() { 357 SpillTest t = new SpillTest(); 358 t.cnt = 3; 359 t.b = new int[3]; 360 t.b[0] = 0; 361 t.b[1] = 1; 362 t.b[2] = 2; 363 int j = 0; 364 t.s = ""; 365 t.test(); 366 } 367 368 @Test 369 // TODO: Make tests more precise 370 @IR(counts = {IRNode.LEA_P, "=2"}, 371 phase = {CompilePhase.FINAL_CODE}, 372 applyIfPlatform = {"mac", "false"}) 373 // Negative test 374 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, ">=2"}, 375 phase = {CompilePhase.FINAL_CODE}, 376 applyIf = {"OptoPeephole", "false"}) 377 @IR(counts = {IRNode.DECODE_HEAP_OOP_NOT_NULL, "<=2"}, 378 phase = {CompilePhase.FINAL_CODE}, 379 applyIf = {"OptoPeephole", "true"}) 380 // Test that the peephole removes a spill. 381 @IR(counts = {IRNode.MEM_TO_REG_SPILL_COPY, ">=18"}, 382 phase = {CompilePhase.FINAL_CODE}, 383 applyIf = {"OptoPeephole", "false"}) 384 @IR(counts = {IRNode.MEM_TO_REG_SPILL_COPY, ">=16"}, 385 phase = {CompilePhase.FINAL_CODE}, 386 applyIf = {"OptoPeephole", "true"}) 387 String test() { 388 String res = ""; 389 for (int i = 0; i < cnt; i++) { 390 if (i != 0) { 391 res = res + "."; 392 } 393 res = res + b[i]; 394 } 395 return res; 396 } 397 }