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