1 /*
2 * Copyright (c) 2014, 2016, Oracle and/or its affiliates. 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 * @test LevelTransitionTest
26 * @summary Test the correctness of compilation level transitions for different methods
27 * @library /test/lib /
28 * @modules java.base/jdk.internal.misc
29 * java.management
30 *
31 * @build sun.hotspot.WhiteBox
32 * @run driver ClassFileInstaller sun.hotspot.WhiteBox
33 * sun.hotspot.WhiteBox$WhiteBoxPermission
34 * @run main/othervm/timeout=240 -Xmixed -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
35 * -XX:+WhiteBoxAPI -XX:+TieredCompilation -XX:-UseCounterDecay
36 * -XX:CompileCommand=compileonly,compiler.whitebox.SimpleTestCaseHelper::*
37 * -XX:CompileCommand=compileonly,compiler.tiered.LevelTransitionTest$ExtendedTestCase$CompileMethodHolder::*
38 * compiler.tiered.TransitionsTestExecutor
39 * compiler.tiered.LevelTransitionTest
40 */
41
42 package compiler.tiered;
43
44 import compiler.whitebox.CompilerWhiteBoxTest;
45 import compiler.whitebox.SimpleTestCase;
46
47 import java.lang.reflect.Executable;
48 import java.lang.reflect.Method;
49 import java.util.Objects;
50 import java.util.concurrent.Callable;
51
52 public class LevelTransitionTest extends TieredLevelsTest {
53 /**
54 * Shows if method was profiled by being executed on levels 2 or 3
55 */
56 protected boolean isMethodProfiled;
57 private int transitionCount;
58
59 public static void main(String[] args) throws Throwable {
60 assert (!CompilerWhiteBoxTest.skipOnTieredCompilation(false));
61
62 CompilerWhiteBoxTest.main(LevelTransitionTest::new, args);
63 // run extended test cases
64 for (TestCase testCase : ExtendedTestCase.values()) {
65 new LevelTransitionTest(testCase).runTest();
66 }
67 }
68
69 protected LevelTransitionTest(TestCase testCase) {
70 super(testCase);
71 isMethodProfiled = testCase.isOsr(); // OSR methods were already profiled by warmup
72 transitionCount = 0;
73 }
74
75 @Override
76 protected void test() throws Exception {
77 checkTransitions();
78 deoptimize();
79 printInfo();
80 if (testCase.isOsr()) {
81 // deoptimization makes the following transitions be unstable
82 // methods go to level 3 before 4 because of uncommon_trap and reprofile
83 return;
84 }
85 checkTransitions();
86 }
87
88 /**
89 * Makes and verifies transitions between compilation levels
90 */
91 protected void checkTransitions() throws Exception {
92 checkNotCompiled();
93 boolean finish = false;
94 while (!finish) {
95 System.out.printf("Level transition #%d%n", ++transitionCount);
96 int newLevel;
97 int current = getCompLevel();
98 int expected = getNextLevel(current);
99 if (current == expected) {
100 // if we are on expected level, just execute it more
101 // to ensure that the level won't change
102 System.out.printf("Method %s is already on expected level %d%n", method, expected);
103 compile();
104 newLevel = getCompLevel();
105 finish = true;
106 } else {
107 newLevel = changeCompLevel();
108 finish = false;
109 }
110 System.out.printf("Method %s is compiled on level %d. Expected level is %d%n", method, newLevel, expected);
111 checkLevel(expected, newLevel);
112 printInfo();
113 }
114 ;
115 }
116
117 /**
118 * Gets next expected level for the test case on each transition.
119 *
120 * @param currentLevel a level the test case is compiled on
121 * @return expected compilation level
122 */
123 protected int getNextLevel(int currentLevel) {
124 int nextLevel = currentLevel;
125 switch (currentLevel) {
126 case CompilerWhiteBoxTest.COMP_LEVEL_NONE:
127 nextLevel = isMethodProfiled ? CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION
128 : CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE;
129 break;
130 case CompilerWhiteBoxTest.COMP_LEVEL_LIMITED_PROFILE:
131 case CompilerWhiteBoxTest.COMP_LEVEL_FULL_PROFILE:
132 nextLevel = CompilerWhiteBoxTest.COMP_LEVEL_FULL_OPTIMIZATION;
133 isMethodProfiled = true;
134 break;
135 }
136 nextLevel = isTrivial() ? CompilerWhiteBoxTest.COMP_LEVEL_SIMPLE : nextLevel;
137 return Math.min(nextLevel, CompilerWhiteBoxTest.TIERED_STOP_AT_LEVEL);
138 }
139
140 /**
141 * Determines if tested method should be handled as trivial
142 *
143 * @return {@code true} for trivial methods, {@code false} otherwise
144 */
145 protected boolean isTrivial() {
146 return testCase == ExtendedTestCase.ACCESSOR_TEST
147 || testCase == SimpleTestCase.METHOD_TEST
148 || testCase == SimpleTestCase.STATIC_TEST
149 || (testCase == ExtendedTestCase.TRIVIAL_CODE_TEST && isMethodProfiled);
150 }
151
152 /**
153 * Invokes {@linkplain #method} until its compilation level is changed.
154 * Note that if the level won't change, it will be an endless loop
155 *
156 * @return compilation level the {@linkplain #method} was compiled on
157 */
158 protected int changeCompLevel() {
159 int currentLevel = getCompLevel();
160 int newLevel = currentLevel;
161 int result = 0;
162 while (currentLevel == newLevel) {
163 result = compile(1);
164 if (WHITE_BOX.isMethodCompiled(method, testCase.isOsr())) {
165 newLevel = getCompLevel();
166 }
167 }
168 return newLevel;
169 }
170
171 protected static class Helper {
172 /**
173 * Gets method from a specified class using its name
174 *
175 * @param aClass type method belongs to
176 * @param name the name of the method
177 * @return {@link Method} that represents corresponding class method
178 */
179 public static Method getMethod(Class<?> aClass, String name) {
180 Method method;
181 try {
182 method = aClass.getDeclaredMethod(name);
183 } catch (NoSuchMethodException e) {
184 throw new Error("TESTBUG: Unable to get method " + name, e);
185 }
186 return method;
187 }
188
189 /**
190 * Gets {@link Callable} that invokes given method from the given object
191 *
192 * @param object the object the specified method is invoked from
193 * @param name the name of the method
194 */
195 public static Callable<Integer> getCallable(Object object, String name) {
196 Method method = getMethod(object.getClass(), name);
197 return () -> {
198 try {
199 return Objects.hashCode(method.invoke(object));
200 } catch (ReflectiveOperationException e) {
201 throw new Error("TESTBUG: Invocation failure", e);
202 }
203 };
204 }
205 }
206
207 private static enum ExtendedTestCase implements CompilerWhiteBoxTest.TestCase {
208 ACCESSOR_TEST("accessor"),
209 NONTRIVIAL_METHOD_TEST("nonTrivialMethod"),
210 TRIVIAL_CODE_TEST("trivialCode");
211
212 private final Executable executable;
213 private final Callable<Integer> callable;
214
215 @Override
216 public Executable getExecutable() {
217 return executable;
218 }
219
220 @Override
221 public Callable<Integer> getCallable() {
222 return callable;
223 }
224
225 @Override
226 public boolean isOsr() {
227 return false;
228 }
229
230 private ExtendedTestCase(String methodName) {
231 this.executable = LevelTransitionTest.Helper.getMethod(CompileMethodHolder.class, methodName);
232 this.callable = LevelTransitionTest.Helper.getCallable(new CompileMethodHolder(), methodName);
233 }
234
235 private static class CompileMethodHolder {
236 private final int iter = 10;
237 private int field = 42;
238
239 /**
240 * Non-trivial method for threshold policy: contains loops
241 */
242 public int nonTrivialMethod() {
243 int acc = 0;
244 for (int i = 0; i < iter; i++) {
245 acc += i;
246 }
247 return acc;
248 }
249
250 /**
251 * Field accessor method
252 */
253 public int accessor() {
254 return field;
255 }
256
257 /**
258 * Method considered as trivial by amount of code
259 */
260 public int trivialCode() {
261 int var = 0xBAAD_C0DE;
262 var *= field;
263 return var;
264 }
265 }
266 }
267
268 }