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