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 }