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