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