Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 class Main1 implements Base {
     18 }
     19 
     20 class Main2 extends Main1 {
     21   public void foobar() {}
     22 }
     23 
     24 class Main3 implements Base {
     25   public int foo(int i) {
     26     if (i != 3) {
     27       printError("error3");
     28     }
     29     return -(i + 10);
     30   }
     31 }
     32 
     33 public class Main {
     34   static Base sMain1;
     35   static Base sMain2;
     36   static Base sMain3;
     37 
     38   static boolean sIsOptimizing = true;
     39   static boolean sHasJIT = true;
     40   static volatile boolean sOtherThreadStarted;
     41 
     42   private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
     43     if (hasSingleImplementation(clazz, method_name) != b) {
     44       System.out.println(clazz + "." + method_name +
     45           " doesn't have single implementation value of " + b);
     46     }
     47   }
     48 
     49   static int getValue(Class<?> cls) {
     50     if (cls == Main1.class || cls == Main2.class) {
     51       return 1;
     52     }
     53     return 3;
     54   }
     55 
     56   // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked.
     57   // So sMain1.foo() can be devirtualized to Base.foo() and be inlined.
     58   // After Dummy.createMain3() which links in Main3, live testImplement() on stack
     59   // should be deoptimized.
     60   static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) {
     61     if (setHasJIT) {
     62       if (isInterpreted()) {
     63         sHasJIT = false;
     64       }
     65       return;
     66     }
     67 
     68     if (createMain3 && (sIsOptimizing || sHasJIT)) {
     69       assertIsManaged();
     70     }
     71 
     72     if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
     73       System.out.println("11 expected.");
     74     }
     75     if (sMain1.$noinline$bar() != -1) {
     76       System.out.println("-1 expected.");
     77     }
     78     if (sMain2.foo(getValue(sMain2.getClass())) != 11) {
     79       System.out.println("11 expected.");
     80     }
     81 
     82     if (createMain3) {
     83       // Wait for the other thread to start.
     84       while (!sOtherThreadStarted);
     85       // Create an Main2 instance and assign it to sMain2.
     86       // sMain1 is kept the same.
     87       sMain3 = Dummy.createMain3();
     88       // Wake up the other thread.
     89       synchronized(Main.class) {
     90         Main.class.notify();
     91       }
     92     } else if (wait) {
     93       // This is the other thread.
     94       synchronized(Main.class) {
     95         sOtherThreadStarted = true;
     96         // Wait for Main2 to be linked and deoptimization is triggered.
     97         try {
     98           Main.class.wait();
     99         } catch (Exception e) {
    100         }
    101       }
    102     }
    103 
    104     // There should be a deoptimization here right after Main3 is linked by
    105     // calling Dummy.createMain3(), even though sMain1 didn't change.
    106     // The behavior here would be different if inline-cache is used, which
    107     // doesn't deoptimize since sMain1 still hits the type cache.
    108     if (sMain1.foo(getValue(sMain1.getClass())) != 11) {
    109       System.out.println("11 expected.");
    110     }
    111     if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) {
    112       // This method should be deoptimized right after Main3 is created.
    113       assertIsInterpreted();
    114     }
    115 
    116     if (sMain3 != null) {
    117       if (sMain3.foo(getValue(sMain3.getClass())) != -13) {
    118         System.out.println("-13 expected.");
    119       }
    120     }
    121   }
    122 
    123   // Test scenarios under which CHA-based devirtualization happens,
    124   // and class loading that implements a method can invalidate compiled code.
    125   public static void main(String[] args) {
    126     System.loadLibrary(args[0]);
    127 
    128     if (isInterpreted()) {
    129       sIsOptimizing = false;
    130     }
    131 
    132     // sMain1 is an instance of Main1.
    133     // sMain2 is an instance of Main2.
    134     // Neither Main1 nor Main2 override default method Base.foo().
    135     // Main3 hasn't bee loaded yet.
    136     sMain1 = new Main1();
    137     sMain2 = new Main2();
    138 
    139     ensureJitCompiled(Main.class, "testImplement");
    140     testImplement(false, false, true);
    141 
    142     if (sHasJIT && !sIsOptimizing) {
    143       assertSingleImplementation(Base.class, "foo", true);
    144       assertSingleImplementation(Main1.class, "foo", true);
    145     } else {
    146       // Main3 is verified ahead-of-time so it's linked in already.
    147     }
    148 
    149     // Create another thread that also calls sMain1.foo().
    150     // Try to test suspend and deopt another thread.
    151     new Thread() {
    152       public void run() {
    153         testImplement(false, true, false);
    154       }
    155     }.start();
    156 
    157     // This will create Main3 instance in the middle of testImplement().
    158     testImplement(true, false, false);
    159     assertSingleImplementation(Base.class, "foo", false);
    160     assertSingleImplementation(Main1.class, "foo", true);
    161     assertSingleImplementation(sMain3.getClass(), "foo", true);
    162   }
    163 
    164   private static native void ensureJitCompiled(Class<?> itf, String method_name);
    165   private static native void assertIsInterpreted();
    166   private static native void assertIsManaged();
    167   private static native boolean isInterpreted();
    168   private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
    169 }
    170 
    171 // Put createMain3() in another class to avoid class loading due to verifier.
    172 class Dummy {
    173   static Base createMain3() {
    174     return new Main3();
    175   }
    176 }
    177