--- old/src/share/vm/prims/whitebox.cpp 2019-05-18 09:46:12.000000000 -0700 +++ new/src/share/vm/prims/whitebox.cpp 2019-05-18 09:46:12.000000000 -0700 @@ -867,19 +867,23 @@ ThreadToNativeFromVM ttn(thread); jclass clazz = env->FindClass(vmSymbols::java_lang_Object()->as_C_string()); CHECK_JNI_EXCEPTION_(env, NULL); - result = env->NewObjectArray(2, clazz, NULL); + result = env->NewObjectArray(3, clazz, NULL); if (result == NULL) { return result; } - jobject obj = integerBox(thread, env, code->comp_level()); + jobject level = integerBox(thread, env, code->comp_level()); CHECK_JNI_EXCEPTION_(env, NULL); - env->SetObjectArrayElement(result, 0, obj); + env->SetObjectArrayElement(result, 0, level); + + jobject id = integerBox(thread, env, code->compile_id()); + CHECK_JNI_EXCEPTION_(env, NULL); + env->SetObjectArrayElement(result, 1, id); jbyteArray insts = env->NewByteArray(insts_size); CHECK_JNI_EXCEPTION_(env, NULL); env->SetByteArrayRegion(insts, 0, insts_size, (jbyte*) code->insts_begin()); - env->SetObjectArrayElement(result, 1, insts); + env->SetObjectArrayElement(result, 2, insts); return result; WB_END --- old/test/compiler/tiered/NonTieredLevelsTest.java 2019-05-18 09:46:13.000000000 -0700 +++ new/test/compiler/tiered/NonTieredLevelsTest.java 2019-05-18 09:46:13.000000000 -0700 @@ -54,9 +54,7 @@ } public static void main(String[] args) throws Exception { - if (TIERED_COMPILATION) { - System.err.println("Test isn't applicable w/ enabled " - + "TieredCompilation. Skip test."); + if (CompilerWhiteBoxTest.skipOnTieredCompilation(true)) { return; } CompilerWhiteBoxTest.main(NonTieredLevelsTest::new, args); --- old/test/compiler/tiered/TieredLevelsTest.java 2019-05-18 09:46:14.000000000 -0700 +++ new/test/compiler/tiered/TieredLevelsTest.java 2019-05-18 09:46:13.000000000 -0700 @@ -34,16 +34,14 @@ * @author igor.ignatyev@oracle.com */ public class TieredLevelsTest extends CompLevelsTest { - public static void main(String[] args) throws Exception { - if (!TIERED_COMPILATION) { - System.err.println("Test isn't applicable w/ disabled " - + "TieredCompilation. Skip test."); + public static void main(String[] args) throws Exception, Throwable { + if (CompilerWhiteBoxTest.skipOnTieredCompilation(false)) { return; } CompilerWhiteBoxTest.main(TieredLevelsTest::new, args); } - private TieredLevelsTest(TestCase testCase) { + protected TieredLevelsTest(TestCase testCase) { super(testCase); // to prevent inlining of #method WHITE_BOX.testSetDontInlineMethod(method, true); @@ -76,14 +74,18 @@ } } - @Override protected void checkLevel(int expected, int actual) { if (expected == COMP_LEVEL_FULL_PROFILE && actual == COMP_LEVEL_LIMITED_PROFILE) { // for simple method full_profile may be replaced by limited_profile + if (IS_VERBOSE) { + System.out.printf("Level check: full profiling was replaced " + + "by limited profiling. Expected: %d, actual:%d", + expected, actual); + } return; } super.checkLevel(expected, actual); - } + } } --- old/test/compiler/whitebox/CompilerWhiteBoxTest.java 2019-05-18 09:46:14.000000000 -0700 +++ new/test/compiler/whitebox/CompilerWhiteBoxTest.java 2019-05-18 09:46:14.000000000 -0700 @@ -41,19 +41,19 @@ */ public abstract class CompilerWhiteBoxTest { /** {@code CompLevel::CompLevel_none} -- Interpreter */ - protected static int COMP_LEVEL_NONE = 0; + protected static final int COMP_LEVEL_NONE = 0; /** {@code CompLevel::CompLevel_any}, {@code CompLevel::CompLevel_all} */ - protected static int COMP_LEVEL_ANY = -1; + protected static final int COMP_LEVEL_ANY = -1; /** {@code CompLevel::CompLevel_simple} -- C1 */ - protected static int COMP_LEVEL_SIMPLE = 1; + protected static final int COMP_LEVEL_SIMPLE = 1; /** {@code CompLevel::CompLevel_limited_profile} -- C1, invocation & backedge counters */ - protected static int COMP_LEVEL_LIMITED_PROFILE = 2; + protected static final int COMP_LEVEL_LIMITED_PROFILE = 2; /** {@code CompLevel::CompLevel_full_profile} -- C1, invocation & backedge counters + mdo */ - protected static int COMP_LEVEL_FULL_PROFILE = 3; + protected static final int COMP_LEVEL_FULL_PROFILE = 3; /** {@code CompLevel::CompLevel_full_optimization} -- C2 or Shark */ - protected static int COMP_LEVEL_FULL_OPTIMIZATION = 4; + protected static final int COMP_LEVEL_FULL_OPTIMIZATION = 4; /** Maximal value for CompLevel */ - protected static int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION; + protected static final int COMP_LEVEL_MAX = COMP_LEVEL_FULL_OPTIMIZATION; /** Instance of WhiteBox */ protected static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); @@ -347,14 +347,22 @@ System.out.printf("%n%s:%n", method); System.out.printf("\tcompilable:\t%b%n", WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, false)); - System.out.printf("\tcompiled:\t%b%n", - WHITE_BOX.isMethodCompiled(method, false)); + boolean isCompiled = WHITE_BOX.isMethodCompiled(method, false); + System.out.printf("\tcompiled:\t%b%n", isCompiled); + if (isCompiled) { + System.out.printf("\tcompile_id:\t%d%n", + NMethod.get(method, false).compile_id); + } System.out.printf("\tcomp_level:\t%d%n", WHITE_BOX.getMethodCompilationLevel(method, false)); System.out.printf("\tosr_compilable:\t%b%n", WHITE_BOX.isMethodCompilable(method, COMP_LEVEL_ANY, true)); - System.out.printf("\tosr_compiled:\t%b%n", - WHITE_BOX.isMethodCompiled(method, true)); + isCompiled = WHITE_BOX.isMethodCompiled(method, true); + System.out.printf("\tosr_compiled:\t%b%n", isCompiled); + if (isCompiled) { + System.out.printf("\tosr_compile_id:\t%d%n", + NMethod.get(method, true).compile_id); + } System.out.printf("\tosr_comp_level:\t%d%n", WHITE_BOX.getMethodCompilationLevel(method, true)); System.out.printf("\tin_queue:\t%b%n", @@ -437,6 +445,22 @@ } return result; } + + /** + * Skip the test for the specified value of Tiered Compilation + * @param value of TieredCompilation the test should not run with + * @return {@code true} if the test should be skipped, + * {@code false} otherwise + */ + protected static boolean skipOnTieredCompilation(boolean value) { + if (value == CompilerWhiteBoxTest.TIERED_COMPILATION) { + System.err.println("Test isn't applicable w/ " + + (value ? "enabled" : "disabled") + + "TieredCompilation. Skip test."); + return true; + } + return false; + } } enum SimpleTestCase implements CompilerWhiteBoxTest.TestCase { --- old/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java 2019-05-18 09:46:15.000000000 -0700 +++ new/test/testlibrary/whitebox/sun/hotspot/code/NMethod.java 2019-05-18 09:46:15.000000000 -0700 @@ -34,18 +34,21 @@ return obj == null ? null : new NMethod(obj); } private NMethod(Object[] obj) { - assert obj.length == 2; + assert obj.length == 3; comp_level = (Integer) obj[0]; - insts = (byte[]) obj[1]; + compile_id = (Integer) obj[1]; + insts = (byte[]) obj[2]; } - public byte[] insts; - public int comp_level; + public final byte[] insts; + public final int comp_level; + public final int compile_id; @Override public String toString() { return "NMethod{" + "insts=" + insts + ", comp_level=" + comp_level + + ", compile_id=" + compile_id + '}'; } } --- /dev/null 2019-05-18 09:46:16.000000000 -0700 +++ new/test/compiler/tiered/ConstantGettersTransitionsTest.java 2019-05-18 09:46:16.000000000 -0700 @@ -0,0 +1,193 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. 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. + */ + +import java.lang.reflect.Executable; +import java.util.concurrent.Callable; + +/** + * @test ConstantGettersTransitionsTest + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build TransitionsTestExecutor ConstantGettersTransitionsTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=240 -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -XX:+TieredCompilation + * -XX:CompileCommand=compileonly,ConstantGettersTestCase$TrivialMethods::* + * TransitionsTestExecutor ConstantGettersTransitionsTest + * @summary Test the correctness of compilation level transitions for constant getters methods + */ +public class ConstantGettersTransitionsTest extends LevelTransitionTest { + public static void main(String[] args) { + assert (!CompilerWhiteBoxTest.skipOnTieredCompilation(false)); + + // run test cases + for (TestCase testCase : ConstantGettersTestCase.values()) { + new ConstantGettersTransitionsTest(testCase).runTest(); + } + } + + @Override + protected boolean isTrivial() { + return true; + } + + private ConstantGettersTransitionsTest(TestCase testCase) { + super(testCase); + } +} + +enum ConstantGettersTestCase implements CompilerWhiteBoxTest.TestCase { + ICONST_M1, + ICONST_0, + ICONST_1, + ICONST_2, + ICONST_3, + ICONST_4, + ICONST_5, + LCONST_0, + LCONST_1, + FCONST_0, + FCONST_1, + FCONST_2, + DCONST_0, + DCONST_1, + DCONST_W, + BYTE, + SHORT, + CHAR; + + private final Executable executable; + private final Callable callable; + + @Override + public Executable getExecutable() { + return executable; + } + + @Override + public Callable getCallable() { + return callable; + } + + @Override + public boolean isOsr() { + return false; + } + + private ConstantGettersTestCase() { + String name = "make" + this.name(); + this.executable = LevelTransitionTest.Helper.getMethod(TrivialMethods.class, name); + this.callable = LevelTransitionTest.Helper.getCallable(new TrivialMethods(), name); + } + + /** + * Contains methods that load constants with certain types of bytecodes + * See JVMS 2.11.2. Load and Store Instructions + * Note that it doesn't have a method for ldc_w instruction + */ + private static class TrivialMethods { + public static int makeICONST_M1() { + return -1; + } + + public static int makeICONST_0() { + return 0; + } + + public static int makeICONST_1() { + return 1; + } + + public static int makeICONST_2() { + return 2; + } + + public static int makeICONST_3() { + return 3; + } + + public static int makeICONST_4() { + return 4; + } + + public static int makeICONST_5() { + return 5; + } + + public static long makeLCONST_0() { + return 0L; + } + + public static long makeLCONST_1() { + return 1L; + } + + public static float makeFCONST_0() { + return 0F; + } + + public static float makeFCONST_1() { + return 1F; + } + + public static float makeFCONST_2() { + return 2F; + } + + public static double makeDCONST_0() { + return 0D; + } + + public static double makeDCONST_1() { + return 1D; + } + + public static double makeDCONST_W() { + // ldc2_w + return Double.MAX_VALUE; + } + + public static Object makeOBJECT() { + // aconst_null + return null; + } + + public static byte makeBYTE() { + // bipush + return (byte) 0x7F; + } + + public static short makeSHORT() { + // sipush + return (short) 0x7FFF; + } + + public static char makeCHAR() { + // ldc + return (char) 0xFFFF; + } + + public static boolean makeBOOLEAN() { + return true; + } + } +} --- /dev/null 2019-05-18 09:46:17.000000000 -0700 +++ new/test/compiler/tiered/LevelTransitionTest.java 2019-05-18 09:46:17.000000000 -0700 @@ -0,0 +1,247 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. 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. + */ + +import java.lang.reflect.Executable; +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.concurrent.Callable; + +/** + * @test LevelTransitionTest + * @library /testlibrary /testlibrary/whitebox /compiler/whitebox + * @build TransitionsTestExecutor LevelTransitionTest + * @run main ClassFileInstaller sun.hotspot.WhiteBox sun.hotspot.WhiteBox$WhiteBoxPermission + * @run main/othervm/timeout=240 -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions + * -XX:+WhiteBoxAPI -XX:+TieredCompilation + * -XX:CompileCommand=compileonly,SimpleTestCase$Helper::* + * -XX:CompileCommand=compileonly,ExtendedTestCase$CompileMethodHolder::* + * TransitionsTestExecutor LevelTransitionTest + * @summary Test the correctness of compilation level transitions for different methods + */ +public class LevelTransitionTest extends TieredLevelsTest { + /** Shows if method was profiled by being executed on levels 2 or 3 */ + protected boolean isMethodProfiled; + private int transitionCount; + + public static void main(String[] args) throws Throwable { + assert (!CompilerWhiteBoxTest.skipOnTieredCompilation(false)); + + CompilerWhiteBoxTest.main(LevelTransitionTest::new, args); + // run extended test cases + for (TestCase testCase : ExtendedTestCase.values()) { + new LevelTransitionTest(testCase).runTest(); + } + } + + protected LevelTransitionTest(TestCase testCase) { + super(testCase); + isMethodProfiled = testCase.isOsr(); // OSR methods were already profiled by warmup + transitionCount = 0; + } + + @Override + protected void test() throws Exception { + checkTransitions(); + deoptimize(); + printInfo(); + if (testCase.isOsr()) { + // deoptimization makes the following transitions be unstable + // methods go to level 3 before 4 because of uncommon_trap and reprofile + return; + } + checkTransitions(); + } + + /** + * Makes and verifies transitions between compilation levels + */ + protected void checkTransitions() { + checkNotCompiled(); + boolean finish = false; + while (!finish) { + System.out.printf("Level transition #%d%n", ++transitionCount); + int newLevel; + int current = getCompLevel(); + int expected = getNextLevel(current); + if (current == expected) { + // if we are on expected level, just execute it more + // to ensure that the level won't change + System.out.printf("Method %s is already on expected level %d%n", method, expected); + compile(); + newLevel = getCompLevel(); + finish = true; + } else { + newLevel = changeCompLevel(); + finish = false; + } + System.out.printf("Method %s is compiled on level %d. Expected level is %d%n", method, newLevel, expected); + checkLevel(expected, newLevel); + printInfo(); + }; + } + + /** + * Gets next expected level for the test case on each transition. + * + * @param currentLevel a level the test case is compiled on + * @return expected compilation level + */ + protected int getNextLevel(int currentLevel) { + int nextLevel = currentLevel; + switch (currentLevel) { + case CompilerWhiteBoxTest.COMP_LEVEL_NONE: + nextLevel = isMethodProfiled ? CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION + : CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE; + break; + case CompilerWhiteBoxTest.COMP_LEVEL_LIMITED_PROFILE: + case CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE: + nextLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION; + isMethodProfiled = true; + break; + } + nextLevel = isTrivial() ? CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE : nextLevel; + return Math.min(nextLevel, CompilerWhiteBoxTest.TIERED_STOP_AT_LEVEL); + } + + /** + * Determines if tested method should be handled as trivial + * + * @return {@code true} for trivial methods, {@code false} otherwise + */ + protected boolean isTrivial() { + return testCase == ExtendedTestCase.ACCESSOR_TEST + || testCase == SimpleTestCase.METHOD_TEST + || testCase == SimpleTestCase.STATIC_TEST + || (testCase == ExtendedTestCase.TRIVIAL_CODE_TEST && isMethodProfiled); + } + + /** + * Invokes {@linkplain #method} until its compilation level is changed. + * Note that if the level won't change, it will be an endless loop + * + * @return compilation level the {@linkplain #method} was compiled on + */ + protected int changeCompLevel() { + int currentLevel = getCompLevel(); + int newLevel = currentLevel; + int result = 0; + while (currentLevel == newLevel) { + result = compile(1); + if (WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) { + newLevel = getCompLevel(); + } + } + return newLevel; + } + + protected static class Helper { + /** + * Gets method from a specified class using its name + * + * @param aClass type method belongs to + * @param name the name of the method + * @return {@link Method} that represents corresponding class method + */ + public static Method getMethod(Class aClass, String name) { + Method method; + try { + method = aClass.getDeclaredMethod(name); + } catch (NoSuchMethodException e) { + throw new Error("TESTBUG: Unable to get method " + name, e); + } + return method; + } + + /** + * Gets {@link Callable} that invokes given method from the given object + * + * @param object the object the specified method is invoked from + * @param name the name of the method + */ + public static Callable getCallable(Object object, String name) { + Method method = getMethod(object.getClass(), name); + return () -> { + try { + return Objects.hashCode(method.invoke(object)); + } catch (ReflectiveOperationException e) { + throw new Error("TESTBUG: Invocation failure", e); + } + }; + } + } +} + +enum ExtendedTestCase implements CompilerWhiteBoxTest.TestCase { + ACCESSOR_TEST("accessor"), + NONTRIVIAL_METHOD_TEST("nonTrivialMethod"), + TRIVIAL_CODE_TEST("trivialCode"); + + private final Executable executable; + private final Callable callable; + + @Override + public Executable getExecutable() { + return executable; + } + + @Override + public Callable getCallable() { + return callable; + } + + @Override + public boolean isOsr() { + return false; + } + + private ExtendedTestCase(String methodName) { + this.executable = LevelTransitionTest.Helper.getMethod(CompileMethodHolder.class, methodName); + this.callable = LevelTransitionTest.Helper.getCallable(new CompileMethodHolder(), methodName); + } + + private static class CompileMethodHolder { + private final int iter = 10; + private int field = 42; + + /** Non-trivial method for threshold policy: contains loops */ + public int nonTrivialMethod() { + int acc = 0; + for (int i = 0; i < iter; i++) { + acc += i; + } + return acc; + } + + /** Field accessor method */ + public int accessor() { + return field; + } + + /** Method considered as trivial by amount of code */ + public int trivialCode() { + int var = 0xBAAD_C0DE; + var *= field; + return var; + } + } +} --- /dev/null 2019-05-18 09:46:17.000000000 -0700 +++ new/test/compiler/tiered/TransitionsTestExecutor.java 2019-05-18 09:46:17.000000000 -0700 @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. 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. + */ + +import com.oracle.java.testlibrary.OutputAnalyzer; +import com.oracle.java.testlibrary.ProcessTools; + +import java.lang.management.ManagementFactory; +import java.lang.management.RuntimeMXBean; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Executes given test in a separate VM with enabled Tiered Compilation for + * CompilationPolicyChoice 2 and 3 + */ +public class TransitionsTestExecutor { + public static void main(String[] args) throws Throwable { + if (CompilerWhiteBoxTest.skipOnTieredCompilation(false)) { + return; + } + if (args.length != 1) { + throw new Error("TESTBUG: Test name should be specified"); + } + executeTestFor(2, args[0]); + executeTestFor(3, args[0]); + } + + private static void executeTestFor(int compilationPolicy, String testName) throws Throwable { + String policy = "-XX:CompilationPolicyChoice=" + compilationPolicy; + + // Get runtime arguments including VM options given to this executor + RuntimeMXBean runtime = ManagementFactory.getRuntimeMXBean(); + List vmArgs = runtime.getInputArguments(); + + // Construct execution command with compilation policy choice and test name + List args = new ArrayList<>(vmArgs); + Collections.addAll(args, policy, testName); + + OutputAnalyzer out = ProcessTools.executeTestJvm(args.toArray(new String[args.size()])); + int exitCode = out.getExitValue(); + if (exitCode != 0) { + throw new Error("Test execution failed with exit code " + exitCode); + } + } +}