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 interface Base {
     18   void foo(int i);
     19   void $noinline$bar();
     20 }
     21 
     22 class Main1 implements Base {
     23   public void foo(int i) {
     24     if (i != 1) {
     25       printError("error1");
     26     }
     27   }
     28 
     29   // Test rewriting invoke-interface into invoke-virtual when inlining fails.
     30   public void $noinline$bar() {
     31     System.out.print("");
     32     System.out.print("");
     33     System.out.print("");
     34     System.out.print("");
     35     System.out.print("");
     36     System.out.print("");
     37     System.out.print("");
     38     System.out.print("");
     39   }
     40 
     41   void printError(String msg) {
     42     System.out.println(msg);
     43   }
     44 }
     45 
     46 class Main2 extends Main1 {
     47   public void foo(int i) {
     48     if (i != 2) {
     49       printError("error2");
     50     }
     51   }
     52 }
     53 
     54 public class Main {
     55   static Base sMain1;
     56   static Base sMain2;
     57 
     58   static boolean sIsOptimizing = true;
     59   static boolean sHasJIT = true;
     60   static volatile boolean sOtherThreadStarted;
     61 
     62   private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) {
     63     if (hasSingleImplementation(clazz, method_name) != b) {
     64       System.out.println(clazz + "." + method_name +
     65           " doesn't have single implementation value of " + b);
     66     }
     67   }
     68 
     69   // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
     70   // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
     71   // After Dummy.createMain2() which links in Main2, live testImplement() on stack
     72   // should be deoptimized.
     73   static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) {
     74     if (setHasJIT) {
     75       if (isInterpreted()) {
     76         sHasJIT = false;
     77       }
     78       return;
     79     }
     80 
     81     if (createMain2 && (sIsOptimizing || sHasJIT)) {
     82       assertIsManaged();
     83     }
     84 
     85     sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
     86     sMain1.$noinline$bar();
     87 
     88     if (createMain2) {
     89       // Wait for the other thread to start.
     90       while (!sOtherThreadStarted);
     91       // Create an Main2 instance and assign it to sMain2.
     92       // sMain1 is kept the same.
     93       sMain2 = Dummy.createMain2();
     94       // Wake up the other thread.
     95       synchronized(Main.class) {
     96         Main.class.notify();
     97       }
     98     } else if (wait) {
     99       // This is the other thread.
    100       synchronized(Main.class) {
    101         sOtherThreadStarted = true;
    102         // Wait for Main2 to be linked and deoptimization is triggered.
    103         try {
    104           Main.class.wait();
    105         } catch (Exception e) {
    106         }
    107       }
    108     }
    109 
    110     // There should be a deoptimization here right after Main2 is linked by
    111     // calling Dummy.createMain2(), even though sMain1 didn't change.
    112     // The behavior here would be different if inline-cache is used, which
    113     // doesn't deoptimize since sMain1 still hits the type cache.
    114     sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
    115     if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) {
    116       // This method should be deoptimized right after Main2 is created.
    117       assertIsInterpreted();
    118     }
    119 
    120     if (sMain2 != null) {
    121       sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
    122     }
    123   }
    124 
    125   // Test scenarios under which CHA-based devirtualization happens,
    126   // and class loading that overrides a method can invalidate compiled code.
    127   public static void main(String[] args) {
    128     System.loadLibrary(args[0]);
    129 
    130     if (isInterpreted()) {
    131       sIsOptimizing = false;
    132     }
    133 
    134     // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
    135     sMain1 = new Main1();
    136 
    137     ensureJitCompiled(Main.class, "testImplement");
    138     testImplement(false, false, true);
    139 
    140     if (sHasJIT && !sIsOptimizing) {
    141       assertSingleImplementation(Base.class, "foo", true);
    142       assertSingleImplementation(Main1.class, "foo", true);
    143     } else {
    144       // Main2 is verified ahead-of-time so it's linked in already.
    145     }
    146 
    147     // Create another thread that also calls sMain1.foo().
    148     // Try to test suspend and deopt another thread.
    149     new Thread() {
    150       public void run() {
    151         testImplement(false, true, false);
    152       }
    153     }.start();
    154 
    155     // This will create Main2 instance in the middle of testImplement().
    156     testImplement(true, false, false);
    157     assertSingleImplementation(Base.class, "foo", false);
    158     assertSingleImplementation(Main1.class, "foo", false);
    159   }
    160 
    161   private static native void ensureJitCompiled(Class<?> itf, String method_name);
    162   private static native void assertIsInterpreted();
    163   private static native void assertIsManaged();
    164   private static native boolean isInterpreted();
    165   private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
    166 }
    167 
    168 // Put createMain2() in another class to avoid class loading due to verifier.
    169 class Dummy {
    170   static Main1 createMain2() {
    171     return new Main2();
    172   }
    173 }
    174