1 /*
2 * Copyright (c) 2018, 2020, Red Hat, Inc. All rights reserved.
3 * Copyright (c) 2021, 2023, 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 /*
26 * common code to run and validate tests of code generation for
27 * volatile ops on AArch64
28 *
29 * incoming args are <testclass> <testtype>
30 *
31 * where <testclass> in {TestVolatileLoad,
32 * TestVolatileStore,
33 * TestUnsafeVolatileLoad,
34 * TestUnsafeVolatileStore,
35 * TestUnsafeVolatileCAS,
36 * TestUnsafeVolatileWeakCAS,
37 * TestUnsafeVolatileCAE,
38 * TestUnsafeVolatileGAS}
39 * and <testtype> in {G1,
40 * Serial,
41 * Parallel,
42 * Shenandoah,
43 * ShenandoahIU}
44 */
45
46
47 package compiler.c2.aarch64;
48
49 import java.util.List;
50 import java.util.ListIterator;
51 import java.util.Iterator;
52 import java.util.regex.Pattern;
53 import java.io.*;
54
55 import jdk.test.lib.Asserts;
56 import jdk.test.lib.compiler.InMemoryJavaCompiler;
57 import jdk.test.lib.process.OutputAnalyzer;
58 import jdk.test.lib.process.ProcessTools;
59 import jdk.test.whitebox.WhiteBox;
60
61 // runner class that spawns a new JVM to exercises a combination of
62 // volatile MemOp and GC. The ops are compiled with the dmb -->
63 // ldar/stlr transforms either enabled or disabled. this runner parses
64 // the PrintOptoAssembly output checking that the generated code is
65 // correct.
66
67 public class TestVolatiles {
68 public void runtest(String classname, String testType) throws Throwable {
69 // n.b. clients omit the package name for the class
70 String fullclassname = "compiler.c2.aarch64." + classname;
71 // build up a command line for the spawned JVM
72 String[] procArgs;
73 int argcount;
74 // add one or two extra arguments according to test type
75 // i.e. GC type plus GC conifg
76 switch(testType) {
77 case "G1":
78 argcount = 9;
79 procArgs = new String[argcount];
80 procArgs[argcount - 2] = "-XX:+UseG1GC";
81 break;
82 case "Parallel":
83 argcount = 9;
84 procArgs = new String[argcount];
85 procArgs[argcount - 2] = "-XX:+UseParallelGC";
86 break;
87 case "Serial":
88 argcount = 9;
89 procArgs = new String[argcount];
90 procArgs[argcount - 2] = "-XX:+UseSerialGC";
91 break;
92 case "Shenandoah":
93 argcount = 10;
94 procArgs = new String[argcount];
95 procArgs[argcount - 3] = "-XX:+UnlockExperimentalVMOptions";
96 procArgs[argcount - 2] = "-XX:+UseShenandoahGC";
97 break;
98 case "ShenandoahIU":
99 argcount = 11;
100 procArgs = new String[argcount];
101 procArgs[argcount - 4] = "-XX:+UnlockExperimentalVMOptions";
102 procArgs[argcount - 3] = "-XX:+UseShenandoahGC";
103 procArgs[argcount - 2] = "-XX:ShenandoahGCMode=iu";
104 break;
105 default:
106 throw new RuntimeException("unexpected test type " + testType);
107 }
108
109 // fill in arguments common to all cases
110
111 // the first round of test enables transform of barriers to
112 // use acquiring loads and releasing stores by setting arg
113 // zero appropriately. this arg is reset in the second run to
114 // disable the transform.
115
116 procArgs[0] = "-XX:+UseCompressedOops";
117 procArgs[1] = "-XX:-BackgroundCompilation";
118 procArgs[2] = "-XX:-TieredCompilation";
119 procArgs[3] = "-XX:+PrintOptoAssembly";
120 procArgs[4] = "-XX:CompileCommand=compileonly," + fullclassname + "::" + "test*";
121 procArgs[5] = "--add-exports";
122 procArgs[6] = "java.base/jdk.internal.misc=ALL-UNNAMED";
123 procArgs[argcount - 1] = fullclassname;
124
125 runtest(classname, testType, true, procArgs);
126
127 if (!classname.equals("TestUnsafeVolatileGAA")) {
128 procArgs[0] = "-XX:-UseCompressedOops";
129 runtest(classname, testType, false, procArgs);
130 }
131 }
132
133
134 public void runtest(String classname, String testType, boolean useCompressedOops, String[] procArgs) throws Throwable {
135 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(procArgs);
136 OutputAnalyzer output = new OutputAnalyzer(pb.start());
137
138 output.stderrShouldBeEmptyIgnoreVMWarnings();
139 output.stdoutShouldNotBeEmpty();
140 output.shouldHaveExitValue(0);
141
142 // check the output for the correct asm sequence as
143 // appropriate to test class, test type and whether transform
144 // was applied
145
146 checkoutput(output, classname, testType, useCompressedOops);
147 }
148
149 // skip through output returning a line containing the desireed
150 // substring or null
151 private String skipTo(Iterator<String> iter, String substring)
152 {
153 while (iter.hasNext()) {
154 String nextLine = iter.next();
155 if (nextLine.matches(".*" + substring + ".*")) {
156 return nextLine;
157 }
158 }
159 return null;
160 }
161
162 // locate the start of compiler output for the desired method and
163 // then check that each expected instruction occurs in the output
164 // in the order supplied. throw an excpetion if not found.
165 // n.b. the spawned JVM's output is included in the exception
166 // message to make it easeir to identify what is missing.
167
168 private boolean checkCompile(Iterator<String> iter, String methodname, String[] expected, OutputAnalyzer output, boolean do_throw)
169 {
170 // trace call to allow eyeball check of what we are checking against
171 System.out.println("checkCompile(" + methodname + ",");
172 String sepr = " { ";
173 for (String s : expected) {
174 System.out.print(sepr);
175 System.out.print(s);
176 sepr = ",\n ";
177 }
178 System.out.println(" })");
179
180 // look for the start of an opto assembly print block
181 String match = skipTo(iter, Pattern.quote("{method}"));
182 if (match == null) {
183 if (do_throw) {
184 throw new RuntimeException("Missing compiler output for " + methodname + "!\n\n" + output.getOutput());
185 }
186 return false;
187 }
188 // check the compiled method name is right
189 match = skipTo(iter, Pattern.quote("- name:"));
190 if (match == null) {
191 if (do_throw) {
192 throw new RuntimeException("Missing compiled method name!\n\n" + output.getOutput());
193 }
194 return false;
195 }
196 if (!match.contains(methodname)) {
197 if (do_throw) {
198 throw new RuntimeException("Wrong method " + match + "!\n -- expecting " + methodname + "\n\n" + output.getOutput());
199 }
200 return false;
201 }
202 // make sure we can match each expected term in order
203 for (String s : expected) {
204 match = skipTo(iter, s);
205 if (match == null) {
206 if (do_throw) {
207 throw new RuntimeException("Missing expected output " + s + "!\n\n" + output.getOutput());
208 }
209 return false;
210 }
211 }
212 return true;
213 }
214
215 // check for expected asm output from a volatile load
216
217 private void checkload(OutputAnalyzer output, String testType, boolean useCompressedOops) throws Throwable
218 {
219 Iterator<String> iter = output.asLines().listIterator();
220
221 // we shoud see this same sequence for normal or unsafe volatile load
222 // for both int and Object fields
223
224 String[] matches;
225 matches = new String[] {
226 "ldarw",
227 "membar_acquire \\(elided\\)",
228 "ret"
229 };
230 checkCompile(iter, "testInt", matches, output, true);
231
232 matches = new String[] {
233 useCompressedOops ? "ldarw?" : "ldar",
234 "membar_acquire \\(elided\\)",
235 "ret"
236 };
237 checkCompile(iter, "testObj", matches, output, true);
238
239 }
240
241 // check for expected asm output from a volatile store
242
243 private void checkstore(OutputAnalyzer output, String testType, boolean useCompressedOops) throws Throwable
244 {
245 Iterator<String> iter = output.asLines().listIterator();
246
247 String[] matches;
248
249 // non object stores are straightforward
250 // this is the sequence of instructions for all cases
251 matches = new String[] {
252 "membar_release \\(elided\\)",
253 "stlrw",
254 "membar_volatile \\(elided\\)",
255 "ret"
256 };
257 checkCompile(iter, "testInt", matches, output, true);
258
259 // object stores will be as above except for when the GC
260 // introduces barriers for card marking
261 switch (testType) {
262 default:
263 // this is the basic sequence of instructions
264 matches = new String[] {
265 "membar_release \\(elided\\)",
266 useCompressedOops ? "stlrw?" : "stlr",
267 "membar_volatile \\(elided\\)",
268 "ret"
269 };
270 break;
271 case "G1":
272 // a card mark volatile barrier should be generated
273 // before the card mark strb
274 //
275 // following the fix for 8225776 the G1 barrier is now
276 // scheduled out of line after the membar volatile and
277 // and subsequent return
278 matches = new String[] {
279 "membar_release \\(elided\\)",
280 useCompressedOops ? "stlrw?" : "stlr",
281 "membar_volatile \\(elided\\)",
282 "ret",
283 "membar_volatile",
284 "dmb ish",
285 "strb"
286 };
287 break;
288 case "Shenandoah":
289 case "ShenandoahIU":
290 // Shenandoah generates normal object graphs for
291 // volatile stores
292 matches = new String[] {
293 "membar_release \\(elided\\)",
294 useCompressedOops ? "stlrw?" : "stlr",
295 "membar_volatile \\(elided\\)",
296 "ret"
297 };
298 break;
299 }
300
301 checkCompile(iter, "testObj", matches, output, true);
302 }
303
304 // check for expected asm output from a volatile cas
305
306 private void checkcas(OutputAnalyzer output, String testType, boolean useCompressedOops) throws Throwable
307 {
308 Iterator<String> iter = output.asLines().listIterator();
309
310 String[] matches;
311 String[][] tests = {
312 { "testInt", "cmpxchgw" },
313 { "testLong", "cmpxchg" },
314 { "testByte", "cmpxchgb" },
315 { "testShort", "cmpxchgs" },
316 };
317
318 for (String[] test : tests) {
319 // non object stores are straightforward
320 // this is the sequence of instructions for all cases
321 matches = new String[] {
322 "membar_release \\(elided\\)",
323 test[1] + "_acq",
324 "membar_acquire \\(elided\\)",
325 "ret"
326 };
327 checkCompile(iter, test[0], matches, output, true);
328 }
329
330 // object stores will be as above except for when the GC
331 // introduces barriers for card marking
332 switch (testType) {
333 default:
334 // this is the basic sequence of instructions
335 matches = new String[] {
336 "membar_release \\(elided\\)",
337 useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
338 "strb",
339 "membar_acquire \\(elided\\)",
340 "ret"
341 };
342 break;
343 case "G1":
344 // a card mark volatile barrier should be generated
345 // before the card mark strb
346 //
347 // following the fix for 8225776 the G1 barrier is now
348 // scheduled out of line after the membar acquire and
349 // and subsequent return
350 matches = new String[] {
351 "membar_release \\(elided\\)",
352 useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
353 "membar_acquire \\(elided\\)",
354 "ret",
355 "membar_volatile",
356 "dmb ish",
357 "strb"
358 };
359 break;
360 case "Shenandoah":
361 case "ShenandoahIU":
362 // For volatile CAS, Shenanodoah generates normal
363 // graphs with a shenandoah-specific cmpxchg
364 matches = new String[] {
365 "membar_release \\(elided\\)",
366 useCompressedOops ? "cmpxchgw?_acq_shenandoah" : "cmpxchg_acq_shenandoah",
367 "membar_acquire \\(elided\\)",
368 "ret"
369 };
370 break;
371 }
372 checkCompile(iter, "testObj", matches, output, true);
373 }
374
375 private void checkcae(OutputAnalyzer output, String testType, boolean useCompressedOops) throws Throwable
376 {
377 ListIterator<String> iter = output.asLines().listIterator();
378
379 String[] matches;
380 String[][] tests = {
381 { "testInt", "cmpxchgw" },
382 { "testLong", "cmpxchg" },
383 { "testByte", "cmpxchgb" },
384 { "testShort", "cmpxchgs" },
385 };
386
387 for (String[] test : tests) {
388 // non object stores are straightforward
389 // this is the sequence of instructions for all cases
390 matches = new String[] {
391 "membar_release \\(elided\\)",
392 test[1] + "_acq",
393 "membar_acquire \\(elided\\)",
394 "ret"
395 };
396 checkCompile(iter, test[0], matches, output, true);
397 }
398
399 // object stores will be as above except for when the GC
400 // introduces barriers for card marking
401 switch (testType) {
402 default:
403 // this is the basic sequence of instructions
404 matches = new String[] {
405 "membar_release \\(elided\\)",
406 "strb",
407 useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
408 "membar_acquire \\(elided\\)",
409 "ret"
410 };
411
412 // card marking store may be scheduled before or after
413 // the cmpxchg so try both sequences.
414 int idx = iter.nextIndex();
415 if (!checkCompile(iter, "testObj", matches, output, false)) {
416 iter = output.asLines().listIterator(idx);
417
418 matches = new String[] {
419 "membar_release \\(elided\\)",
420 useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
421 "strb",
422 "membar_acquire \\(elided\\)",
423 "ret"
424 };
425
426 checkCompile(iter, "testObj", matches, output, true);
427 }
428 return;
429
430 case "G1":
431 // a card mark volatile barrier should be generated
432 // before the card mark strb
433 //
434 // following the fix for 8225776 the G1 barrier is now
435 // scheduled out of line after the membar acquire and
436 // and subsequent return
437 matches = new String[] {
438 "membar_release \\(elided\\)",
439 useCompressedOops ? "cmpxchgw?_acq" : "cmpxchg_acq",
440 "membar_acquire \\(elided\\)",
441 "ret",
442 "membar_volatile",
443 "dmb ish",
444 "strb"
445 };
446 break;
447 case "Shenandoah":
448 case "ShenandoahIU":
449 // For volatile CAS, Shenanodoah generates normal
450 // graphs with a shenandoah-specific cmpxchg
451 matches = new String[] {
452 "membar_release \\(elided\\)",
453 useCompressedOops ? "cmpxchgw?_acq_shenandoah" : "cmpxchg_acq_shenandoah",
454 "membar_acquire \\(elided\\)",
455 "ret"
456 };
457 break;
458 }
459 checkCompile(iter, "testObj", matches, output, true);
460 }
461
462 private void checkgas(OutputAnalyzer output, String testType, boolean useCompressedOops) throws Throwable
463 {
464 Iterator<String> iter = output.asLines().listIterator();
465
466 String[] matches;
467 String[][] tests = {
468 { "testInt", "atomic_xchgw" },
469 { "testLong", "atomic_xchg" },
470 };
471
472 for (String[] test : tests) {
473 // non object stores are straightforward
474 // this is the sequence of instructions for all cases
475 matches = new String[] {
476 "membar_release \\(elided\\)",
477 test[1] + "_acq",
478 "membar_acquire \\(elided\\)",
479 "ret"
480 };
481 checkCompile(iter, test[0], matches, output, true);
482 }
483
484 // object stores will be as above except for when the GC
485 // introduces barriers for card marking
486 switch (testType) {
487 default:
488 // this is the basic sequence of instructions
489 matches = new String[] {
490 "membar_release \\(elided\\)",
491 useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq",
492 "strb",
493 "membar_acquire \\(elided\\)",
494 "ret"
495 };
496 break;
497 case "G1":
498 // a card mark volatile barrier should be generated
499 // before the card mark strb
500 //
501 // following the fix for 8225776 the G1 barrier is now
502 // scheduled out of line after the membar acquire and
503 // and subsequent return
504 matches = new String[] {
505 "membar_release \\(elided\\)",
506 useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq",
507 "membar_acquire \\(elided\\)",
508 "ret",
509 "membar_volatile",
510 "dmb ish",
511 "strb"
512 };
513 break;
514 case "Shenandoah":
515 case "ShenandoahIU":
516 matches = new String[] {
517 "membar_release \\(elided\\)",
518 useCompressedOops ? "atomic_xchgw?_acq" : "atomic_xchg_acq",
519 "membar_acquire \\(elided\\)",
520 "ret"
521 };
522 break;
523 }
524
525 checkCompile(iter, "testObj", matches, output, true);
526 }
527
528 private void checkgaa(OutputAnalyzer output, String testType) throws Throwable
529 {
530 Iterator<String> iter = output.asLines().listIterator();
531
532 String[] matches;
533 String[][] tests = {
534 { "testInt", "get_and_addI" },
535 { "testLong", "get_and_addL" },
536 };
537
538 for (String[] test : tests) {
539 // non object stores are straightforward
540 // this is the sequence of instructions for all cases
541 matches = new String[] {
542 "membar_release \\(elided\\)",
543 test[1] + "_acq",
544 "membar_acquire \\(elided\\)",
545 "ret"
546 };
547 checkCompile(iter, test[0], matches, output, true);
548 }
549
550 }
551
552 // perform a check appropriate to the classname
553
554 private void checkoutput(OutputAnalyzer output, String classname, String testType, boolean useCompressedOops) throws Throwable
555 {
556 // trace call to allow eyeball check of what is being checked
557 System.out.println("checkoutput(" +
558 classname + ", " +
559 testType + ")\n" +
560 output.getOutput());
561
562 switch (classname) {
563 case "TestVolatileLoad":
564 checkload(output, testType, useCompressedOops);
565 break;
566 case "TestVolatileStore":
567 checkstore(output, testType, useCompressedOops);
568 break;
569 case "TestUnsafeVolatileLoad":
570 checkload(output, testType, useCompressedOops);
571 break;
572 case "TestUnsafeVolatileStore":
573 checkstore(output, testType, useCompressedOops);
574 break;
575 case "TestUnsafeVolatileCAS":
576 case "TestUnsafeVolatileWeakCAS":
577 checkcas(output, testType, useCompressedOops);
578 break;
579 case "TestUnsafeVolatileCAE":
580 checkcae(output, testType, useCompressedOops);
581 break;
582 case "TestUnsafeVolatileGAS":
583 checkgas(output, testType, useCompressedOops);
584 break;
585 case "TestUnsafeVolatileGAA":
586 checkgaa(output, testType);
587 break;
588 }
589 }
590 }