1 /*
2  * Copyright (C) 2018 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 /**
18  * Tests for detecting throwing methods for code sinking.
19  */
20 public class Main {
21 
22   //
23   // Some "runtime library" methods.
24   //
25 
doThrow(String par)26   static private void doThrow(String par) {
27     throw new Error("you are null: " + par);
28   }
29 
checkNotNullDirect(Object obj, String par)30   static private void checkNotNullDirect(Object obj, String par) {
31     if (obj == null)
32       throw new Error("you are null: " + par);
33   }
34 
checkNotNullSplit(Object obj, String par)35   static private void checkNotNullSplit(Object obj, String par) {
36     if (obj == null)
37       doThrow(par);
38   }
39 
checkNotNullSplitAlt(Object obj, String par)40   static private void checkNotNullSplitAlt(Object obj, String par) {
41     if (obj != null)
42       return;
43     doThrow(par);
44   }
45 
46   //
47   // Various ways of enforcing non-null parameter.
48   // In all cases, par should be subject to code sinking.
49   //
50 
51   /// CHECK-START: void Main.doit1(int[]) code_sinking (before)
52   /// CHECK: begin_block
53   /// CHECK:   <<Str:l\d+>> LoadString
54   /// CHECK:   <<Tst:z\d+>> Equal
55   /// CHECK:                If [<<Tst>>]
56   /// CHECK: end_block
57   /// CHECK: begin_block
58   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
59   /// CHECK:                Throw
60   /// CHECK: end_block
61   //
62   /// CHECK-START: void Main.doit1(int[]) code_sinking (after)
63   /// CHECK: begin_block
64   /// CHECK:   <<Tst:z\d+>> Equal
65   /// CHECK:                If [<<Tst>>]
66   /// CHECK: end_block
67   /// CHECK: begin_block
68   /// CHECK:   <<Str:l\d+>> LoadString
69   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
70   /// CHECK:                Throw
71   /// CHECK: end_block
doit1(int[] a)72   static public void doit1(int[] a) {
73     // Being in the boot image means we know the load string cannot throw. Create one that is
74     // unlikely to be there to ensure we handle that case.
75     String par = "stringUnlikelyToBeInBootImage";
76     if (a == null)
77       throw new Error("you are null: " + par);
78     for (int i = 0; i < a.length; i++) {
79       a[i] = 1;
80     }
81   }
82 
83   /// CHECK-START: void Main.doit2(int[]) code_sinking (before)
84   /// CHECK: begin_block
85   /// CHECK:   <<Str:l\d+>> LoadString
86   /// CHECK:   <<Tst:z\d+>> NotEqual
87   /// CHECK:                If [<<Tst>>]
88   /// CHECK: end_block
89   /// CHECK: begin_block
90   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
91   /// CHECK: end_block
92   //
93   /// CHECK-START: void Main.doit2(int[]) code_sinking (after)
94   /// CHECK: begin_block
95   /// CHECK:   <<Tst:z\d+>> NotEqual
96   /// CHECK:                If [<<Tst>>]
97   /// CHECK: end_block
98   /// CHECK: begin_block
99   /// CHECK:   <<Str:l\d+>> LoadString
100   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
101   /// CHECK: end_block
doit2(int[] a)102   static public void doit2(int[] a) {
103     // Being in the boot image means we know the load string cannot throw. Create one that is
104     // unlikely to be there to ensure we handle that case.
105     String par = "stringUnlikelyToBeInBootImage";
106     if (a == null)
107       doThrow(par);
108     for (int i = 0; i < a.length; i++) {
109       a[i] = 2;
110     }
111   }
112 
113   /// CHECK-START: void Main.doit3(int[]) code_sinking (before)
114   /// CHECK: begin_block
115   /// CHECK:   <<Str:l\d+>> LoadString
116   /// CHECK:   <<Tst:z\d+>> Equal
117   /// CHECK:                If [<<Tst>>]
118   /// CHECK: end_block
119   /// CHECK: begin_block
120   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
121   /// CHECK:                Throw
122   /// CHECK: end_block
123   //
124   /// CHECK-START: void Main.doit3(int[]) code_sinking (after)
125   /// CHECK: begin_block
126   /// CHECK:   <<Tst:z\d+>> Equal
127   /// CHECK:                If [<<Tst>>]
128   /// CHECK: end_block
129   /// CHECK: begin_block
130   /// CHECK:   <<Str:l\d+>> LoadString
131   /// CHECK:                InvokeVirtual [{{l\d+}},<<Str>>]
132   /// CHECK:                Throw
133   /// CHECK: end_block
doit3(int[] a)134   static public void doit3(int[] a) {
135     // Being in the boot image means we know the load string cannot throw. Create one that is
136     // unlikely to be there to ensure we handle that case.
137     String par = "stringUnlikelyToBeInBootImage";
138     checkNotNullDirect(a, par);
139     for (int i = 0; i < a.length; i++) {
140       a[i] = 3;
141     }
142   }
143 
144   /// CHECK-START: void Main.doit4(int[]) code_sinking (before)
145   /// CHECK: begin_block
146   /// CHECK:   <<Str:l\d+>> LoadString
147   /// CHECK:   <<Tst:z\d+>> NotEqual
148   /// CHECK:                If [<<Tst>>]
149   /// CHECK: end_block
150   /// CHECK: begin_block
151   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
152   /// CHECK: end_block
153   //
154   /// CHECK-START: void Main.doit4(int[]) code_sinking (after)
155   /// CHECK: begin_block
156   /// CHECK:   <<Tst:z\d+>> NotEqual
157   /// CHECK:                If [<<Tst>>]
158   /// CHECK: end_block
159   /// CHECK: begin_block
160   /// CHECK:   <<Str:l\d+>> LoadString
161   /// CHECK:                InvokeStaticOrDirect [<<Str>>] method_name:Main.doThrow
162   /// CHECK: end_block
doit4(int[] a)163   static public void doit4(int[] a) {
164     // Being in the boot image means we know the load string cannot throw. Create one that is
165     // unlikely to be there to ensure we handle that case.
166     String par = "stringUnlikelyToBeInBootImage";
167     checkNotNullSplit(a, par);  // resembles Kotlin runtime lib
168                                 // (test is lined, doThrow is not)
169     for (int i = 0; i < a.length; i++) {
170       a[i] = 4;
171     }
172   }
173 
174   // Ensures Phi values are merged properly.
doit5(int[] a)175   static public int doit5(int[] a) {
176     int t = 100;
177     // Being in the boot image means we know the load string cannot throw. Create one that is
178     // unlikely to be there to ensure we handle that case.
179     String par = "stringUnlikelyToBeInBootImage";
180     if (a == null) {
181       doThrow(par);
182     } else {
183       t = 1000;
184     }
185     for (int i = 0; i < a.length; i++) {
186       a[i] = 5;
187     }
188     // Phi on t, even though doThrow never reaches.
189     return t;
190   }
191 
192   //
193   // Various ways of exploiting non-null parameter.
194   // In all cases, implicit null checks are redundant.
195   //
196 
197   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (before)
198   /// CHECK:   <<Par:l\d+>>   ParameterValue
199   /// CHECK:   <<Zero:i\d+>>  IntConstant 0
200   /// CHECK:   <<Null:l\d+>>  NullCheck [<<Par>>]
201   /// CHECK:   <<Len:i\d+>>   ArrayLength [<<Null>>]
202   /// CHECK:   <<Check:i\d+>> BoundsCheck [<<Zero>>,<<Len>>]
203   /// CHECK:   <<Get:i\d+>>   ArrayGet [<<Null>>,<<Check>>]
204   /// CHECK:                  Return [<<Get>>]
205   //
206   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
207   /// CHECK:   <<Par:l\d+>>   ParameterValue
208   /// CHECK:   <<Zero:i\d+>>  IntConstant 0
209   /// CHECK:   <<BT:l\d+>>    BoundType [<<Par>>]
210   /// CHECK:   <<Len:i\d+>>   ArrayLength [<<BT>>]
211   /// CHECK:   <<Check:i\d+>> BoundsCheck [<<Zero>>,<<Len>>]
212   /// CHECK:   <<Get:i\d+>>   ArrayGet [<<BT>>,<<Check>>]
213   /// CHECK:                  Return [<<Get>>]
214   //
215   /// CHECK-START: int Main.deleteNullCheck(int[]) dead_code_elimination$after_inlining (after)
216   /// CHECK-NOT:              NullCheck
deleteNullCheck(int[] a)217   static public int deleteNullCheck(int[] a) {
218     checkNotNullSplit(a, "stringUnlikelyToBeInBootImage");
219     return a[0];
220   }
221 
222   /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (before)
223   /// CHECK:     NullCheck
224   //
225   /// CHECK-START: int Main.deleteNullCheckAlt(int[]) dead_code_elimination$after_inlining (after)
226   /// CHECK-NOT: NullCheck
deleteNullCheckAlt(int[] a)227   static public int deleteNullCheckAlt(int[] a) {
228     checkNotNullSplitAlt(a, "stringUnlikeltyToBeInBootImage");
229     return a[0];
230   }
231 
232   /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (before)
233   /// CHECK:     NullCheck
234   /// CHECK:     NullCheck
235   /// CHECK:     NullCheck
236   //
237   /// CHECK-START: int Main.deleteNullChecks3(int[], int[], int[]) dead_code_elimination$after_inlining (after)
238   /// CHECK-NOT: NullCheck
deleteNullChecks3(int[] a, int[] b, int[] c)239   static public int deleteNullChecks3(int[] a, int[] b, int[] c) {
240     checkNotNullSplit(a, "stringUnlikelytoBeInBootImage1");
241     checkNotNullSplit(b, "stringUnlikelytoBeInBootImage2");
242     checkNotNullSplit(c, "stringUnlikelytoBeInBootImage3");
243     return a[0] + b[0] + c[0];
244   }
245 
246   //
247   // Test driver.
248   //
249 
main(String[] args)250   static public void main(String[] args) {
251     int[] a = new int[100];
252     for (int i = 0; i < 100; i++) {
253       a[i] = 0;
254     }
255 
256     try {
257       doit1(null);
258       System.out.println("should not reach this!");
259     } catch (Error e) {
260       doit1(a);
261     }
262     for (int i = 0; i < 100; i++) {
263       expectEquals(1, a[i]);
264     }
265 
266     try {
267       doit2(null);
268       System.out.println("should not reach this!");
269     } catch (Error e) {
270       doit2(a);
271     }
272     for (int i = 0; i < 100; i++) {
273       expectEquals(2, a[i]);
274     }
275 
276     try {
277       doit3(null);
278       System.out.println("should not reach this!");
279     } catch (Error e) {
280       doit3(a);
281     }
282     for (int i = 0; i < 100; i++) {
283       expectEquals(3, a[i]);
284     }
285 
286     try {
287       doit4(null);
288       System.out.println("should not reach this!");
289     } catch (Error e) {
290       doit4(a);
291     }
292     for (int i = 0; i < 100; i++) {
293       expectEquals(4, a[i]);
294     }
295 
296     try {
297       doit5(null);
298       System.out.println("should not reach this!");
299     } catch (Error e) {
300       expectEquals(1000, doit5(a));
301     }
302     for (int i = 0; i < 100; i++) {
303       expectEquals(5, a[i]);
304     }
305 
306     int[] x = { 11 } ;
307     expectEquals(11, deleteNullCheck(x));
308     int[] y = { 55 } ;
309     int[] z = { 22 } ;
310     expectEquals(88, deleteNullChecks3(x, y, z));
311 
312     try {
313       deleteNullCheck(null);
314       System.out.println("should not reach this!");
315     } catch (Error e) {
316     }
317 
318     System.out.println("passed");
319   }
320 
expectEquals(int expected, int result)321   private static void expectEquals(int expected, int result) {
322     if (expected != result) {
323       throw new Error("Expected: " + expected + ", found: " + result);
324     }
325   }
326 }
327