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 SubA extends Super {
getValue()18   int getValue() { return 42; }
someSubclassesThrow()19   void someSubclassesThrow() throws Error { throw new Error("I always throw"); }
20 }
21 
22 class SubB extends Super {
getValue()23   int getValue() { return 38; }
someSubclassesThrow()24   void someSubclassesThrow() { System.out.println("I don't throw"); }
25 }
26 
27 class SubD extends Super {
getValue()28   int getValue() { return 10; }
someSubclassesThrow()29   void someSubclassesThrow() { System.out.println("I don't throw"); }
30 }
31 
32 class SubE extends Super {
getValue()33   int getValue() { return -4; }
someSubclassesThrow()34   void someSubclassesThrow() { System.out.println("I don't throw"); }
35 }
36 
37 public class Main {
38 
39   /// CHECK-START: int Main.inlineMonomorphicSubA(Super) inliner (before)
40   /// CHECK:       InvokeVirtual method_name:Super.getValue
41 
42   /// CHECK-START: int Main.inlineMonomorphicSubA(Super) inliner (after)
43   /// CHECK:  <<SubARet:i\d+>>      IntConstant 42
44   /// CHECK:  <<Obj:l\d+>>          NullCheck
45   /// CHECK:  <<ObjClass:l\d+>>     InstanceFieldGet [<<Obj>>] field_name:java.lang.Object.shadow$_klass_
46   /// CHECK:  <<InlineClass:l\d+>>  LoadClass class_name:SubA
47   /// CHECK:  <<Test:z\d+>>         NotEqual [<<InlineClass>>,<<ObjClass>>]
48   /// CHECK:  <<DefaultRet:i\d+>>   InvokeVirtual [<<Obj>>] method_name:Super.getValue
49 
50   /// CHECK:  <<Ret:i\d+>>          Phi [<<SubARet>>,<<DefaultRet>>]
51   /// CHECK:                        Return [<<Ret>>]
52 
53   /// CHECK-NOT:                    Deoptimize
inlineMonomorphicSubA(Super a)54   public static int inlineMonomorphicSubA(Super a) {
55     return a.getValue();
56   }
57 
58   /// CHECK-START: int Main.inlinePolymophicSubASubB(Super) inliner (before)
59   /// CHECK:       InvokeVirtual method_name:Super.getValue
60 
61   // Note that the order in which the types are added to the inline cache in the profile matters.
62 
63   /// CHECK-START: int Main.inlinePolymophicSubASubB(Super) inliner (after)
64   /// CHECK-DAG:  <<SubARet:i\d+>>          IntConstant 42
65   /// CHECK-DAG:  <<SubBRet:i\d+>>          IntConstant 38
66   /// CHECK-DAG:   <<Obj:l\d+>>             NullCheck
67   /// CHECK-DAG:   <<ObjClassSubA:l\d+>>    InstanceFieldGet [<<Obj>>] field_name:java.lang.Object.shadow$_klass_
68   /// CHECK-DAG:   <<InlineClassSubA:l\d+>> LoadClass class_name:SubA
69   /// CHECK-DAG:   <<TestSubA:z\d+>>        NotEqual [<<InlineClassSubA>>,<<ObjClassSubA>>]
70   /// CHECK-DAG:                            If [<<TestSubA>>]
71 
72   /// CHECK-DAG:   <<ObjClassSubB:l\d+>>    InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
73   /// CHECK-DAG:   <<InlineClassSubB:l\d+>> LoadClass class_name:SubB
74   /// CHECK-DAG:   <<TestSubB:z\d+>>        NotEqual [<<InlineClassSubB>>,<<ObjClassSubB>>]
75   /// CHECK-DAG:   <<DefaultRet:i\d+>>      InvokeVirtual [<<Obj>>] method_name:Super.getValue
76 
77   /// CHECK-DAG:  <<FirstMerge:i\d+>>       Phi [<<SubBRet>>,<<DefaultRet>>]
78   /// CHECK-DAG:  <<Ret:i\d+>>              Phi [<<SubARet>>,<<FirstMerge>>]
79   /// CHECK-DAG:                            Return [<<Ret>>]
80 
81   /// CHECK-NOT:                            Deoptimize
inlinePolymophicSubASubB(Super a)82   public static int inlinePolymophicSubASubB(Super a) {
83     return a.getValue();
84   }
85 
86   /// CHECK-START: int Main.inlinePolymophicCrossDexSubASubC(Super) inliner (before)
87   /// CHECK:       InvokeVirtual method_name:Super.getValue
88 
89   // Note that the order in which the types are added to the inline cache in the profile matters.
90 
91   /// CHECK-START: int Main.inlinePolymophicCrossDexSubASubC(Super) inliner (after)
92   /// CHECK-DAG:  <<SubARet:i\d+>>          IntConstant 42
93   /// CHECK-DAG:  <<SubCRet:i\d+>>          IntConstant 24
94   /// CHECK-DAG:  <<Obj:l\d+>>              NullCheck
95   /// CHECK-DAG:  <<ObjClassSubA:l\d+>>     InstanceFieldGet [<<Obj>>] field_name:java.lang.Object.shadow$_klass_
96   /// CHECK-DAG:  <<InlineClassSubA:l\d+>>  LoadClass class_name:SubA
97   /// CHECK-DAG:  <<TestSubA:z\d+>>         NotEqual [<<InlineClassSubA>>,<<ObjClassSubA>>]
98   /// CHECK-DAG:                            If [<<TestSubA>>]
99 
100   /// CHECK-DAG:  <<ObjClassSubC:l\d+>>     InstanceFieldGet field_name:java.lang.Object.shadow$_klass_
101   /// CHECK-DAG:  <<InlineClassSubC:l\d+>>  LoadClass class_name:SubC
102   /// CHECK-DAG:  <<TestSubC:z\d+>>         NotEqual [<<InlineClassSubC>>,<<ObjClassSubC>>]
103   /// CHECK-DAG:  <<DefaultRet:i\d+>>       InvokeVirtual [<<Obj>>] method_name:Super.getValue
104 
105   /// CHECK-DAG:  <<FirstMerge:i\d+>>       Phi [<<SubCRet>>,<<DefaultRet>>]
106   /// CHECK-DAG:  <<Ret:i\d+>>              Phi [<<SubARet>>,<<FirstMerge>>]
107   /// CHECK-DAG:                            Return [<<Ret>>]
108 
109   /// CHECK-NOT:                            Deoptimize
inlinePolymophicCrossDexSubASubC(Super a)110   public static int inlinePolymophicCrossDexSubASubC(Super a) {
111     return a.getValue();
112   }
113 
114   /// CHECK-START: int Main.inlineMegamorphic(Super) inliner (before)
115   /// CHECK:       InvokeVirtual method_name:Super.getValue
116 
117   /// CHECK-START: int Main.inlineMegamorphic(Super) inliner (after)
118   /// CHECK:       InvokeVirtual method_name:Super.getValue
inlineMegamorphic(Super a)119   public static int inlineMegamorphic(Super a) {
120     return a.getValue();
121   }
122 
123   /// CHECK-START: int Main.inlineMissingTypes(Super) inliner (before)
124   /// CHECK:       InvokeVirtual method_name:Super.getValue
125 
126   /// CHECK-START: int Main.inlineMissingTypes(Super) inliner (after)
127   /// CHECK:       InvokeVirtual method_name:Super.getValue
inlineMissingTypes(Super a)128   public static int inlineMissingTypes(Super a) {
129     return a.getValue();
130   }
131 
132   /// CHECK-START: int Main.noInlineCache(Super) inliner (before)
133   /// CHECK:       InvokeVirtual method_name:Super.getValue
134 
135   /// CHECK-START: int Main.noInlineCache(Super) inliner (after)
136   /// CHECK:       InvokeVirtual method_name:Super.getValue
noInlineCache(Super a)137   public static int noInlineCache(Super a) {
138     return a.getValue();
139   }
140 
141   // We shouldn't inline `someSubclassesThrow` since we are trying a monomorphic inline and it
142   // always throws for SubA. However, we shouldn't mark it as `always_throws` since we speculatively
143   // tried to inline and other subclasses (e.g. SubB) can call noInlineSomeSubclassesThrow and they
144   // don't throw.
145 
146   /// CHECK-START: void Main.noInlineSomeSubclassesThrow(Super) inliner (before)
147   /// CHECK:       InvokeVirtual method_name:Super.someSubclassesThrow always_throws:false
148 
149   /// CHECK-START: void Main.noInlineSomeSubclassesThrow(Super) inliner (after)
150   /// CHECK:       InvokeVirtual method_name:Super.someSubclassesThrow always_throws:false
noInlineSomeSubclassesThrow(Super a)151   public static void noInlineSomeSubclassesThrow(Super a) throws Error {
152     a.someSubclassesThrow();
153   }
154 
testInlineMonomorphic()155   public static void testInlineMonomorphic() {
156     if (inlineMonomorphicSubA(new SubA()) != 42) {
157       throw new Error("Expected 42");
158     }
159 
160     // Call with a different type than the one from the inline cache.
161     if (inlineMonomorphicSubA(new SubB()) != 38) {
162       throw new Error("Expected 38");
163     }
164   }
165 
testInlinePolymorhic()166   public static void testInlinePolymorhic() {
167     if (inlinePolymophicSubASubB(new SubA()) != 42) {
168       throw new Error("Expected 42");
169     }
170 
171     if (inlinePolymophicSubASubB(new SubB()) != 38) {
172       throw new Error("Expected 38");
173     }
174 
175     // Call with a different type than the one from the inline cache.
176     if (inlinePolymophicSubASubB(new SubC()) != 24) {
177       throw new Error("Expected 25");
178     }
179 
180     if (inlinePolymophicCrossDexSubASubC(new SubA()) != 42) {
181       throw new Error("Expected 42");
182     }
183 
184     if (inlinePolymophicCrossDexSubASubC(new SubC()) != 24) {
185       throw new Error("Expected 24");
186     }
187 
188     // Call with a different type than the one from the inline cache.
189     if (inlinePolymophicCrossDexSubASubC(new SubB()) != 38) {
190       throw new Error("Expected 38");
191     }
192   }
193 
testInlineMegamorphic()194   public static void testInlineMegamorphic() {
195     if (inlineMegamorphic(new SubA()) != 42) {
196       throw new Error("Expected 42");
197     }
198   }
199 
200 
testNoInlineCache()201   public static void testNoInlineCache() {
202     if (noInlineCache(new SubA()) != 42) {
203       throw new Error("Expected 42");
204     }
205   }
206 
$noinline$testsomeSubclassesThrow()207   private static void $noinline$testsomeSubclassesThrow() throws Exception {
208     try {
209       noInlineSomeSubclassesThrow(new SubA());
210       throw new Exception("Unreachable");
211     } catch (Error expected) {
212     }
213     noInlineSomeSubclassesThrow(new SubB());
214   }
215 
main(String[] args)216   public static void main(String[] args) throws Exception {
217     testInlineMonomorphic();
218     testInlinePolymorhic();
219     testInlineMegamorphic();
220     testNoInlineCache();
221     $noinline$testsomeSubclassesThrow();
222   }
223 }
224