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