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 { 18 main(String[] args)19 public static void main(String[] args) { 20 testSimpleUse(); 21 testTwoUses(); 22 testFieldStores(doThrow); 23 testFieldStoreCycle(); 24 testArrayStores(); 25 testOnlyStoreUses(); 26 testNoUse(); 27 testPhiInput(); 28 testVolatileStore(); 29 doThrow = true; 30 try { 31 testInstanceSideEffects(); 32 } catch (Error e) { 33 // expected 34 System.out.println(e.getMessage()); 35 } 36 try { 37 testStaticSideEffects(); 38 } catch (Error e) { 39 // expected 40 System.out.println(e.getMessage()); 41 } 42 43 try { 44 testStoreStore(doThrow); 45 } catch (Error e) { 46 // expected 47 System.out.println(e.getMessage()); 48 } 49 } 50 51 /// CHECK-START: void Main.testSimpleUse() code_sinking (before) 52 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object 53 /// CHECK: <<New:l\d+>> NewInstance [<<LoadClass>>] 54 /// CHECK: ConstructorFence [<<New>>] 55 /// CHECK: If 56 /// CHECK: begin_block 57 /// CHECK: Throw 58 59 /// CHECK-START: void Main.testSimpleUse() code_sinking (after) 60 /// CHECK-NOT: NewInstance 61 /// CHECK: If 62 /// CHECK: begin_block 63 /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error 64 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object 65 /// CHECK-NOT: begin_block 66 /// CHECK: <<New:l\d+>> NewInstance [<<LoadClass>>] 67 /// CHECK: ConstructorFence [<<New>>] 68 /// CHECK-NOT: begin_block 69 /// CHECK: NewInstance [<<Error>>] 70 /// CHECK: Throw testSimpleUse()71 public static void testSimpleUse() { 72 Object o = new Object(); 73 if (doThrow) { 74 throw new Error(o.toString()); 75 } 76 } 77 78 /// CHECK-START: void Main.testTwoUses() code_sinking (before) 79 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object 80 /// CHECK: NewInstance [<<LoadClass>>] 81 /// CHECK: If 82 /// CHECK: begin_block 83 /// CHECK: Throw 84 85 /// CHECK-START: void Main.testTwoUses() code_sinking (after) 86 /// CHECK-NOT: NewInstance 87 /// CHECK: If 88 /// CHECK: begin_block 89 /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error 90 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object 91 /// CHECK-NOT: begin_block 92 /// CHECK: NewInstance [<<LoadClass>>] 93 /// CHECK-NOT: begin_block 94 /// CHECK: NewInstance [<<Error>>] 95 /// CHECK: Throw testTwoUses()96 public static void testTwoUses() { 97 Object o = new Object(); 98 if (doThrow) { 99 throw new Error(o.toString() + o.toString()); 100 } 101 } 102 103 // NB It might seem that we'd move the allocation and ifield-set but those are 104 // already moved into the throw block by a combo of partial-LSE and DCE. 105 // Instead all that is actually moved is the LoadClass. Also note the 106 // LoadClass can only be moved since it refers to the 'Main' class itself, 107 // meaning there's no need for any clinit/actual loading. 108 // 109 /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before) 110 /// CHECK: <<Int42:i\d+>> IntConstant 42 111 /// CHECK: begin_block 112 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 113 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 114 /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] 115 /// CHECK: Throw 116 117 /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after) 118 /// CHECK: <<Int42:i\d+>> IntConstant 42 119 /// CHECK-NOT: NewInstance 120 /// CHECK: If 121 /// CHECK: begin_block 122 /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error 123 /// CHECK-NOT: begin_block 124 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 125 /// CHECK-NOT: begin_block 126 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 127 /// CHECK-NOT: begin_block 128 /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] 129 /// CHECK-NOT: begin_block 130 /// CHECK: <<Throw:l\d+>> NewInstance [<<Error>>] 131 /// CHECK-NOT: begin_block 132 /// CHECK: Throw [<<Throw>>] testFieldStores(boolean doThrow)133 public static void testFieldStores(boolean doThrow) { 134 Main m = new Main(); 135 m.intField = 42; 136 if (doThrow) { 137 throw new Error(m.toString()); 138 } 139 } 140 141 /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before) 142 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 143 /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>] 144 /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>] 145 /// CHECK: InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>] 146 /// CHECK: InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>] 147 /// CHECK: If 148 /// CHECK: begin_block 149 /// CHECK: Throw 150 151 // TODO(ngeoffray): Handle allocation/store cycles. 152 /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after) 153 /// CHECK: begin_block 154 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 155 /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>] 156 /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>] 157 /// CHECK: InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>] 158 /// CHECK: InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>] 159 /// CHECK: If 160 /// CHECK: begin_block 161 /// CHECK: Throw testFieldStoreCycle()162 public static void testFieldStoreCycle() { 163 Main m1 = new Main(); 164 Main m2 = new Main(); 165 m1.objectField = m2; 166 m2.objectField = m1; 167 if (doThrow) { 168 throw new Error(m1.toString() + m2.toString()); 169 } 170 } 171 172 /// CHECK-START: void Main.testArrayStores() code_sinking (before) 173 /// CHECK: <<Int1:i\d+>> IntConstant 1 174 /// CHECK: <<Int0:i\d+>> IntConstant 0 175 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] 176 /// CHECK: <<NewArray:l\d+>> NewArray [<<LoadClass>>,<<Int1>>] 177 /// CHECK: ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>] 178 /// CHECK: If 179 /// CHECK: begin_block 180 /// CHECK: Throw 181 182 /// CHECK-START: void Main.testArrayStores() code_sinking (after) 183 /// CHECK: <<Int1:i\d+>> IntConstant 1 184 /// CHECK: <<Int0:i\d+>> IntConstant 0 185 /// CHECK-NOT: NewArray 186 /// CHECK: If 187 /// CHECK: begin_block 188 /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error 189 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] 190 /// CHECK-NOT: begin_block 191 /// CHECK: <<NewArray:l\d+>> NewArray [<<LoadClass>>,<<Int1>>] 192 /// CHECK-NOT: begin_block 193 /// CHECK: ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>] 194 /// CHECK-NOT: begin_block 195 /// CHECK: NewInstance [<<Error>>] 196 /// CHECK: Throw testArrayStores()197 public static void testArrayStores() { 198 Object[] o = new Object[1]; 199 o[0] = o; 200 if (doThrow) { 201 throw new Error(o.toString()); 202 } 203 } 204 205 // Make sure code sinking does not crash on dead allocations. testOnlyStoreUses()206 public static void testOnlyStoreUses() { 207 Main m = new Main(); 208 Object[] o = new Object[1]; // dead allocation, should eventually be removed b/35634932. 209 o[0] = m; 210 o = null; // Avoid environment uses for the array allocation. 211 if (doThrow) { 212 throw new Error(m.toString()); 213 } 214 } 215 216 // Make sure code sinking does not crash on dead code. testNoUse()217 public static void testNoUse() { 218 Main m = new Main(); 219 boolean load = Main.doLoop; // dead code, not removed because of environment use. 220 // Ensure one environment use for the static field 221 $opt$noinline$foo(); 222 load = false; 223 if (doThrow) { 224 throw new Error(m.toString()); 225 } 226 } 227 228 // Make sure we can move code only used by a phi. 229 /// CHECK-START: void Main.testPhiInput() code_sinking (before) 230 /// CHECK: <<Null:l\d+>> NullConstant 231 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object 232 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 233 /// CHECK: If 234 /// CHECK: begin_block 235 /// CHECK: Phi [<<Null>>,<<NewInstance>>] 236 /// CHECK: Throw 237 238 /// CHECK-START: void Main.testPhiInput() code_sinking (after) 239 /// CHECK: <<Null:l\d+>> NullConstant 240 /// CHECK-NOT: NewInstance 241 /// CHECK: If 242 /// CHECK: begin_block 243 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object 244 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 245 /// CHECK: begin_block 246 /// CHECK: Phi [<<Null>>,<<NewInstance>>] 247 /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error 248 /// CHECK: NewInstance [<<Error>>] 249 /// CHECK: Throw testPhiInput()250 public static void testPhiInput() { 251 Object f = new Object(); 252 if (doThrow) { 253 Object o = null; 254 int i = 2; 255 if (doLoop) { 256 o = f; 257 i = 42; 258 } 259 throw new Error(o.toString() + i); 260 } 261 } 262 $opt$noinline$foo()263 static void $opt$noinline$foo() {} 264 265 // Check that we do not move volatile stores. 266 /// CHECK-START: void Main.testVolatileStore() code_sinking (before) 267 /// CHECK: <<Int42:i\d+>> IntConstant 42 268 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 269 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 270 /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] 271 /// CHECK: If 272 /// CHECK: begin_block 273 /// CHECK: Throw 274 275 /// CHECK-START: void Main.testVolatileStore() code_sinking (after) 276 /// CHECK: <<Int42:i\d+>> IntConstant 42 277 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 278 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 279 /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] 280 /// CHECK: If 281 /// CHECK: begin_block 282 /// CHECK: Throw testVolatileStore()283 public static void testVolatileStore() { 284 Main m = new Main(); 285 m.volatileField = 42; 286 if (doThrow) { 287 throw new Error(m.toString()); 288 } 289 } 290 testInstanceSideEffects()291 public static void testInstanceSideEffects() { 292 int a = mainField.intField; 293 $noinline$changeIntField(); 294 if (doThrow) { 295 throw new Error("" + a); 296 } 297 } 298 $noinline$changeIntField()299 static void $noinline$changeIntField() { 300 mainField.intField = 42; 301 } 302 testStaticSideEffects()303 public static void testStaticSideEffects() { 304 Object o = obj; 305 $noinline$changeStaticObjectField(); 306 if (doThrow) { 307 throw new Error(o.getClass().toString()); 308 } 309 } 310 $noinline$changeStaticObjectField()311 static void $noinline$changeStaticObjectField() { 312 obj = new Main(); 313 } 314 315 // Test that we preserve the order of stores. 316 // NB It might seem that we'd move the allocation and ifield-set but those are 317 // already moved into the throw block by a combo of partial-LSE and DCE. 318 // Instead all that is actually moved is the LoadClass. Also note the 319 // LoadClass can only be moved since it refers to the 'Main' class itself, 320 // meaning there's no need for any clinit/actual loading. 321 // 322 /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before) 323 /// CHECK: <<Int42:i\d+>> IntConstant 42 324 /// CHECK: <<Int43:i\d+>> IntConstant 43 325 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 326 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 327 /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int42>>] 328 /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int43>>] 329 /// CHECK: Throw 330 /// CHECK-NOT: InstanceFieldSet 331 332 /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after) 333 /// CHECK: <<Int42:i\d+>> IntConstant 42 334 /// CHECK: <<Int43:i\d+>> IntConstant 43 335 /// CHECK-NOT: NewInstance 336 /// CHECK: If 337 /// CHECK: begin_block 338 /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error 339 /// CHECK-NOT: begin_block 340 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main 341 /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] 342 /// CHECK-NOT: begin_block 343 /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int42>>] 344 /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int43>>] 345 /// CHECK-NOT: begin_block 346 /// CHECK: NewInstance [<<Error>>] 347 /// CHECK: Throw 348 /// CHECK-NOT: InstanceFieldSet testStoreStore(boolean doThrow)349 public static void testStoreStore(boolean doThrow) { 350 Main m = new Main(); 351 m.intField = 42; 352 m.intField2 = 43; 353 if (doThrow) { 354 throw new Error(m.$opt$noinline$toString()); 355 } 356 } 357 doStaticNativeCallLiveVreg()358 static native void doStaticNativeCallLiveVreg(); 359 360 // Test ensures that 'o' has been moved into the if despite the InvokeStaticOrDirect. 361 // 362 /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (before) 363 /// CHECK: <<Int1:i\d+>> IntConstant 1 364 /// CHECK: <<Int0:i\d+>> IntConstant 0 365 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] 366 /// CHECK-NOT: begin_block 367 /// CHECK: NewArray [<<LoadClass>>,<<Int1>>] 368 /// CHECK: If 369 /// CHECK: begin_block 370 /// CHECK: Throw 371 372 /// CHECK-START: void Main.testSinkingOverInvoke() code_sinking (after) 373 /// CHECK: <<Int1:i\d+>> IntConstant 1 374 /// CHECK: <<Int0:i\d+>> IntConstant 0 375 /// CHECK: If 376 /// CHECK: begin_block 377 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] 378 /// CHECK: NewArray [<<LoadClass>>,<<Int1>>] 379 /// CHECK: Throw testSinkingOverInvoke()380 static void testSinkingOverInvoke() { 381 Object[] o = new Object[1]; 382 o[0] = o; 383 doStaticNativeCallLiveVreg(); 384 if (doThrow) { 385 throw new Error(o.toString()); 386 } 387 } 388 $opt$noinline$toString()389 public String $opt$noinline$toString() { 390 return "" + intField; 391 } 392 393 volatile int volatileField; 394 int intField; 395 int intField2; 396 Object objectField; 397 static boolean doThrow; 398 static boolean doLoop; 399 static Main mainField = new Main(); 400 static Object obj = new Object(); 401 } 402