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