diff -r ce53844224b6 -r 551c340ca01a test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/jtreg/compiler/c2/aarch64/TestVolatiles.java Thu Jun 28 10:09:58 2018 +0000 @@ -0,0 +1,572 @@ +/* + * Copyright (c) 2018, Red Hat, Inc. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * common code to run and validate tests of code generation for + * volatile ops on AArch64 + * + * incoming args are + * + * where in {TestVolatileLoad, + * TestVolatileStore, + * TestUnsafeVolatileLoad, + * TestUnsafeVolatileStore, + * TestUnsafeVolatileCAS} + * and in {G1, + * CMS, + * CMSCondCardMark, + * Serial, + * Parallel} + */ + + +package compiler.c2.aarch64; + +import java.util.List; +import java.util.Iterator; +import java.io.*; + +import jdk.test.lib.Asserts; +import jdk.test.lib.compiler.InMemoryJavaCompiler; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +// runner class that spawns a new JVM to exercises a combination of +// volatile MemOp and GC. The ops are compiled with the dmb --> +// ldar/stlr transforms either enabled or disabled. this runner parses +// the PrintOptoAssembly output checking that the generated code is +// correct. + +public class TestVolatiles { + public void runtest(String classname, String testType) throws Throwable { + // n.b. clients omit the package name for the class + String fullclassname = "compiler.c2.aarch64." + classname; + // build up a command line for the spawned JVM + String[] procArgs; + int argcount; + // add one or two extra arguments according to test type + // i.e. GC type plus GC conifg + switch(testType) { + case "G1": + argcount = 8; + procArgs = new String[argcount]; + procArgs[argcount - 2] = "-XX:+UseG1GC"; + break; + case "Parallel": + argcount = 8; + procArgs = new String[argcount]; + procArgs[argcount - 2] = "-XX:+UseParallelGC"; + break; + case "Serial": + argcount = 8; + procArgs = new String[argcount]; + procArgs[argcount - 2] = "-XX:+UseSerialGC"; + break; + case "CMS": + argcount = 9 ; + procArgs = new String[argcount]; + procArgs[argcount - 3] = "-XX:+UseConcMarkSweepGC"; + procArgs[argcount - 2] = "-XX:-UseCondCardMark"; + break; + case "CMSCondMark": + argcount = 9 ; + procArgs = new String[argcount]; + procArgs[argcount - 3] = "-XX:+UseConcMarkSweepGC"; + procArgs[argcount - 2] = "-XX:+UseCondCardMark"; + break; + default: + throw new RuntimeException("unexpected test type " + testType); + } + + // fill in arguments common to all cases + + // the first round of test enables transform of barriers to + // use acquiring loads and releasing stores by setting arg + // zero appropriately. this arg is reset in the second run to + // disable the transform. + + procArgs[0] = "-XX:-UseBarriersForVolatile"; + + procArgs[1] = "-XX:-TieredCompilation"; + procArgs[2] = "-XX:+PrintOptoAssembly"; + procArgs[3] = "-XX:CompileCommand=compileonly," + fullclassname + "::" + "test*"; + procArgs[4] = "--add-exports"; + procArgs[5] = "java.base/jdk.internal.misc=ALL-UNNAMED"; + procArgs[argcount - 1] = fullclassname; + + ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(procArgs); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + + output.stderrShouldBeEmptyIgnoreVMWarnings(); + output.stdoutShouldNotBeEmpty(); + output.shouldHaveExitValue(0); + + // check the output for the correct asm sequence as + // appropriate to test class, test type and whether transform + // was applied + + checkoutput(output, classname, testType, false); + + // rerun the test class without the transform applied and + // check the alternative generation is as expected + + procArgs[0] = "-XX:+UseBarriersForVolatile"; + + pb = ProcessTools.createJavaProcessBuilder(procArgs); + output = new OutputAnalyzer(pb.start()); + + output.stderrShouldBeEmptyIgnoreVMWarnings(); + output.stdoutShouldNotBeEmpty(); + output.shouldHaveExitValue(0); + + // again check the output for the correct asm sequence + + checkoutput(output, classname, testType, true); + } + + // skip through output returning a line containing the desireed + // substring or null + private String skipTo(Iterator iter, String substring) + { + while (iter.hasNext()) { + String nextLine = iter.next(); + if (nextLine.contains(substring)) { + return nextLine; + } + } + return null; + } + + // locate the start of compiler output for the desired method and + // then check that each expected instruction occurs in the output + // in the order supplied. throw an excpetion if not found. + // n.b. the spawned JVM's output is included in the exception + // message to make it easeir to identify what is missing. + + private void checkCompile(Iterator iter, String methodname, String[] expected, OutputAnalyzer output) + { + // trace call to allow eyeball check of what we are checking against + System.out.println("checkCompile(" + methodname + ","); + String sepr = " { "; + for (String s : expected) { + System.out.print(sepr); + System.out.print(s); + sepr = ",\n "; + } + System.out.println(" })"); + + // look for the start of an opto assembly print block + String match = skipTo(iter, "{method}"); + if (match == null) { + throw new RuntimeException("Missing compiler output for " + methodname + "!\n\n" + output.getOutput()); + } + // check the compiled method name is right + match = skipTo(iter, "- name:"); + if (match == null) { + throw new RuntimeException("Missing compiled method name!\n\n" + output.getOutput()); + } + if (!match.contains(methodname)) { + throw new RuntimeException("Wrong method " + match + "!\n -- expecting " + methodname + "\n\n" + output.getOutput()); + } + // make sure we can match each expected term in order + for (String s : expected) { + match = skipTo(iter, s); + if (match == null) { + throw new RuntimeException("Missing expected output " + s + "!\n\n" + output.getOutput()); + } + } + } + + // check for expected asm output from a volatile load + + private void checkload(OutputAnalyzer output, String testType, boolean useBarriersForVolatile) throws Throwable + { + Iterator iter = output.asLines().listIterator(); + + // we shoud see this same sequence for normal or unsafe volatile load + // for both int and Object fields + + String[] matches; + + if (!useBarriersForVolatile) { + matches = new String[] { + "ldarw", + "membar_acquire (elided)", + "ret" + }; + } else { + matches = new String[] { + "ldrw", + "membar_acquire", + "dmb ish", + "ret" + }; + } + + checkCompile(iter, "testInt", matches, output); + + checkCompile(iter, "testObj", matches, output) ; + + } + + // check for expected asm output from a volatile store + + private void checkstore(OutputAnalyzer output, String testType, boolean useBarriersForVolatile) throws Throwable + { + Iterator iter = output.asLines().listIterator(); + + String[] matches; + + // non object stores are straightforward + if (!useBarriersForVolatile) { + // this is the sequence of instructions for all cases + matches = new String[] { + "membar_release (elided)", + "stlrw", + "membar_volatile (elided)", + "ret" + }; + } else { + // this is the alternative sequence of instructions + matches = new String[] { + "membar_release", + "dmb ish", + "strw", + "membar_volatile", + "dmb ish", + "ret" + }; + } + + checkCompile(iter, "testInt", matches, output); + + // object stores will be as above except for when the GC + // introduces barriers for card marking + + if (!useBarriersForVolatile) { + switch (testType) { + default: + // this is the basic sequence of instructions + matches = new String[] { + "membar_release (elided)", + "stlrw", + "membar_volatile (elided)", + "ret" + }; + break; + case "G1": + // a card mark volatile barrier should be generated + // before the card mark strb + matches = new String[] { + "membar_release (elided)", + "stlrw", + "membar_volatile", + "dmb ish", + "strb", + "membar_volatile (elided)", + "ret" + }; + break; + case "CMSCondCardMark": + // a card mark volatile barrier should be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be elided + matches = new String[] { + "membar_release (elided)", + "stlrw", + "membar_volatile", + "dmb ish", + "storestore (elided)", + "strb", + "membar_volatile (elided)", + "ret" + }; + break; + case "CMS": + // a volatile card mark membar should not be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be elided + matches = new String[] { + "membar_release (elided)", + "stlrw", + "storestore (elided)", + "strb", + "membar_volatile (elided)", + "ret" + }; + break; + } + } else { + switch (testType) { + default: + // this is the basic sequence of instructions + matches = new String[] { + "membar_release", + "dmb ish", + "strw", + "membar_volatile", + "dmb ish", + "ret" + }; + break; + case "G1": + // a card mark volatile barrier should be generated + // before the card mark strb + matches = new String[] { + "membar_release", + "dmb ish", + "strw", + "membar_volatile", + "dmb ish", + "strb", + "membar_volatile", + "dmb ish", + "ret" + }; + break; + case "CMSCondCardMark": + // a card mark volatile barrier should be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be elided + matches = new String[] { + "membar_release", + "dmb ish", + "strw", + "membar_volatile", + "dmb ish", + "storestore (elided)", + "strb", + "membar_volatile", + "dmb ish", + "ret" + }; + break; + case "CMS": + // a volatile card mark membar should not be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be generated + // as "dmb ishst" + matches = new String[] { + "membar_release", + "dmb ish", + "strw", + "storestore", + "dmb ishst", + "strb", + "membar_volatile", + "dmb ish", + "ret" + }; + break; + } + } + + checkCompile(iter, "testObj", matches, output); + } + + // check for expected asm output from a volatile cas + + private void checkcas(OutputAnalyzer output, String testType, boolean useBarriersForVolatile) throws Throwable + { + Iterator iter = output.asLines().listIterator(); + + String[] matches; + + // non object stores are straightforward + if (!useBarriersForVolatile) { + // this is the sequence of instructions for all cases + matches = new String[] { + "membar_release (elided)", + "cmpxchgw_acq", + "membar_acquire (elided)", + "ret" + }; + } else { + // this is the alternative sequence of instructions + matches = new String[] { + "membar_release", + "dmb ish", + "cmpxchgw", + "membar_acquire", + "dmb ish", + "ret" + }; + } + + checkCompile(iter, "testInt", matches, output); + + // object stores will be as above except for when the GC + // introduces barriers for card marking + + if (!useBarriersForVolatile) { + switch (testType) { + default: + // this is the basic sequence of instructions + matches = new String[] { + "membar_release (elided)", + "cmpxchgw_acq", + "strb", + "membar_acquire (elided)", + "ret" + }; + break; + case "G1": + // a card mark volatile barrier should be generated + // before the card mark strb + matches = new String[] { + "membar_release (elided)", + "cmpxchgw_acq", + "membar_volatile", + "dmb ish", + "strb", + "membar_acquire (elided)", + "ret" + }; + break; + case "CMSCondCardMark": + // a card mark volatile barrier should be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be elided + matches = new String[] { + "membar_release (elided)", + "cmpxchgw_acq", + "membar_volatile", + "dmb ish", + "storestore (elided)", + "strb", + "membar_acquire (elided)", + "ret" + }; + break; + case "CMS": + // a volatile card mark membar should not be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be elided + matches = new String[] { + "membar_release (elided)", + "cmpxchgw_acq", + "storestore (elided)", + "strb", + "membar_acquire (elided)", + "ret" + }; + break; + } + } else { + switch (testType) { + default: + // this is the basic sequence of instructions + matches = new String[] { + "membar_release", + "dmb ish", + "cmpxchgw", + "membar_acquire", + "dmb ish", + "ret" + }; + break; + case "G1": + // a card mark volatile barrier should be generated + // before the card mark strb + matches = new String[] { + "membar_release", + "dmb ish", + "cmpxchgw", + "membar_volatile", + "dmb ish", + "strb", + "membar_acquire", + "dmb ish", + "ret" + }; + break; + case "CMSCondCardMark": + // a card mark volatile barrier should be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be elided + matches = new String[] { + "membar_release", + "dmb ish", + "cmpxchgw", + "membar_volatile", + "dmb ish", + "storestore (elided)", + "strb", + "membar_acquire", + "dmb ish", + "ret" + }; + break; + case "CMS": + // a volatile card mark membar should not be generated + // before the card mark strb from the StoreCM and the + // storestore barrier from the StoreCM should be generated + // as "dmb ishst" + matches = new String[] { + "membar_release", + "dmb ish", + "cmpxchgw", + "storestore", + "dmb ishst", + "strb", + "membar_acquire", + "dmb ish", + "ret" + }; + break; + } + } + + checkCompile(iter, "testObj", matches, output); + } + + // perform a check appropriate to the classname + + private void checkoutput(OutputAnalyzer output, String classname, String testType, boolean useBarriersForVolatile) throws Throwable + { + // trace call to allow eyeball check of what is being checked + System.out.println("checkoutput(" + + classname + ", " + + testType + ", " + + useBarriersForVolatile + ")\n" + + output.getOutput()); + + switch (classname) { + case "TestVolatileLoad": + checkload(output, testType, useBarriersForVolatile); + break; + case "TestVolatileStore": + checkstore(output, testType, useBarriersForVolatile); + break; + case "TestUnsafeVolatileLoad": + checkload(output, testType, useBarriersForVolatile); + break; + case "TestUnsafeVolatileStore": + checkstore(output, testType, useBarriersForVolatile); + break; + case "TestUnsafeVolatileCAS": + checkcas(output, testType, useBarriersForVolatile); + break; + } + } +}