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 public class Main {
main(String[] args)18   public static void main(String[] args) throws Exception {
19     System.loadLibrary(args[0]);
20 
21     $noinline$intUpdate(new Main());
22     ensureJitCompiled(Main.class, "$noinline$intUpdate");
23     $noinline$intUpdate(new SubMain());
24     if (myIntStatic != 5000) {
25       throw new Error("Expected 5000, got " + myIntStatic);
26     }
27 
28     $noinline$objectUpdate(new Main());
29     ensureJitCompiled(Main.class, "$noinline$objectUpdate");
30     $noinline$objectUpdate(new SubMain());
31 
32     $noinline$loopIncrement(new Main());
33     ensureJitCompiled(Main.class, "$noinline$loopIncrement");
34     $noinline$loopIncrement(new SubMain());
35 
36     $noinline$objectReturned(new Main());
37     ensureJitCompiled(Main.class, "$noinline$objectReturned");
38     Object o = $noinline$objectReturned(new SubMain());
39     // We used to get 0xebadde09 in 'o' here and therefore crash
40     // both interpreter and compiled code.
41     if (o instanceof Cloneable) {
42       System.out.println("Unexpected object type " + o.getClass());
43     }
44   }
45 
doCheck()46   public boolean doCheck() {
47     return false;
48   }
49 
$noinline$intUpdate(Main m)50   public static void $noinline$intUpdate(Main m) {
51     int a = 0;
52     // We used to kill 'a' when the inline cache of 'doCheck' only
53     // contains 'Main' (which makes the only branch using 'a' dead).
54     // So the deoptimization at the inline cache was incorrectly assuming
55     // 'a' was dead.
56     for (int i = 0; i < 5000; i++) {
57       if (m.doCheck()) {
58         a++;
59         // We make this branch the only true user of the 'a' phi. All other uses
60         // of 'a' are phi updates.
61         myIntStatic = a;
62       } else if (myIntStatic == 42) {
63         a = 1;
64       }
65     }
66   }
67 
$noinline$objectUpdate(Main m)68   public static void $noinline$objectUpdate(Main m) {
69     Object o = new Object();
70     // We used to kill 'o' when the inline cache of 'doCheck' only
71     // contains 'Main' (which makes the only branch using 'o' dead).
72     // So the deoptimization at the inline cache was incorrectly assuming
73     // 'o' was dead.
74     // This lead to a NPE on the 'toString' call just after deoptimizing.
75     for (int i = 0; i < 5000; i++) {
76       if (m.doCheck()) {
77         // We make this branch the only true user of the 'o' phi. All other uses
78         // of 'o' are phi updates.
79         o.toString();
80       } else if (myIntStatic == 42) {
81         o = m;
82       }
83     }
84   }
85 
$noinline$loopIncrement(Main m)86   public static void $noinline$loopIncrement(Main m) {
87     int k = 0;
88     // We used to kill 'k' and replace it with 5000 when the inline cache
89     // of 'doCheck' only contains 'Main'.
90     // So the deoptimization at the inline cache was incorrectly assuming
91     // 'k' was 5000.
92     for (int i = 0; i < 5000; i++, k++) {
93       if (m.doCheck()) {
94         // We make this branch the only true user of the 'k' phi. All other uses
95         // of 'k' are phi updates.
96         myIntStatic = k;
97       }
98     }
99     if (k != 5000) {
100       throw new Error("Expected 5000, got " + k);
101     }
102   }
103 
$noinline$objectReturned(Main m)104   public static Object $noinline$objectReturned(Main m) {
105     Object o = new Object();
106     // We used to kill 'o' when the inline cache of 'doCheck' only
107     // contains 'Main' (which makes the only branch using 'o' dead).
108     // So the deoptimization at the inline cache was incorrectly assuming
109     // 'o' was dead.
110     // We also need to make 'o' escape through a return instruction, as mterp
111     // executes the same code for return and return-object, and the 0xebadde09
112     // sentinel for dead value is only pushed to non-object dex registers.
113     Object myReturnValue = null;
114     for (int i = 0; i < 5000; i++) {
115       if (m.doCheck()) {
116         // We make this branch the only true user of the 'o' phi. All other uses
117         // of 'o' are phi updates.
118         myReturnValue = o;
119       } else if (myIntStatic == 42) {
120         o = m;
121       }
122     }
123     return myReturnValue;
124   }
125 
126   public static int myIntStatic = 0;
127 
ensureJitCompiled(Class<?> itf, String name)128   public static native void ensureJitCompiled(Class<?> itf, String name);
129 }
130 
131 class SubMain extends Main {
doCheck()132   public boolean doCheck() {
133     return true;
134   }
135 }
136