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 }