/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ public class Main { public static void main(String[] args) { testSimpleUse(); testTwoUses(); testFieldStores(doThrow); testFieldStoreCycle(); testArrayStores(); testOnlyStoreUses(); testNoUse(); testPhiInput(); testVolatileStore(); doThrow = true; try { testInstanceSideEffects(); } catch (Error e) { // expected System.out.println(e.getMessage()); } try { testStaticSideEffects(); } catch (Error e) { // expected System.out.println(e.getMessage()); } try { testStoreStore(doThrow); } catch (Error e) { // expected System.out.println(e.getMessage()); } } /// CHECK-START: void Main.testSimpleUse() code_sinking (before) /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object /// CHECK: NewInstance [<<LoadClass>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testSimpleUse() code_sinking (after) /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<LoadClass>>] /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw public static void testSimpleUse() { Object o = new Object(); if (doThrow) { throw new Error(o.toString()); } } /// CHECK-START: void Main.testTwoUses() code_sinking (before) /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object /// CHECK: NewInstance [<<LoadClass>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testTwoUses() code_sinking (after) /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<LoadClass>>] /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw public static void testTwoUses() { Object o = new Object(); if (doThrow) { throw new Error(o.toString() + o.toString()); } } /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK-NOT: begin_block /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK-NOT: begin_block /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw public static void testFieldStores(boolean doThrow) { Main m = new Main(); m.intField = 42; if (doThrow) { throw new Error(m.toString()); } } /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (before) /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>] /// CHECK: InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw // TODO(ngeoffray): Handle allocation/store cycles. /// CHECK-START: void Main.testFieldStoreCycle() code_sinking (after) /// CHECK: begin_block /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK: <<NewInstance1:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: <<NewInstance2:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance1>>,<<NewInstance2>>] /// CHECK: InstanceFieldSet [<<NewInstance2>>,<<NewInstance1>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw public static void testFieldStoreCycle() { Main m1 = new Main(); Main m2 = new Main(); m1.objectField = m2; m2.objectField = m1; if (doThrow) { throw new Error(m1.toString() + m2.toString()); } } /// CHECK-START: void Main.testArrayStores() code_sinking (before) /// CHECK: <<Int1:i\d+>> IntConstant 1 /// CHECK: <<Int0:i\d+>> IntConstant 0 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] /// CHECK: <<NewArray:l\d+>> NewArray [<<LoadClass>>,<<Int1>>] /// CHECK: ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testArrayStores() code_sinking (after) /// CHECK: <<Int1:i\d+>> IntConstant 1 /// CHECK: <<Int0:i\d+>> IntConstant 0 /// CHECK-NOT: NewArray /// CHECK: If /// CHECK: begin_block /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object[] /// CHECK-NOT: begin_block /// CHECK: <<NewArray:l\d+>> NewArray [<<LoadClass>>,<<Int1>>] /// CHECK-NOT: begin_block /// CHECK: ArraySet [<<NewArray>>,<<Int0>>,<<NewArray>>] /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw public static void testArrayStores() { Object[] o = new Object[1]; o[0] = o; if (doThrow) { throw new Error(o.toString()); } } // Make sure code sinking does not crash on dead allocations. public static void testOnlyStoreUses() { Main m = new Main(); Object[] o = new Object[1]; // dead allocation, should eventually be removed b/35634932. o[0] = m; o = null; // Avoid environment uses for the array allocation. if (doThrow) { throw new Error(m.toString()); } } // Make sure code sinking does not crash on dead code. public static void testNoUse() { Main m = new Main(); boolean load = Main.doLoop; // dead code, not removed because of environment use. // Ensure one environment use for the static field $opt$noinline$foo(); load = false; if (doThrow) { throw new Error(m.toString()); } } // Make sure we can move code only used by a phi. /// CHECK-START: void Main.testPhiInput() code_sinking (before) /// CHECK: <<Null:l\d+>> NullConstant /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Phi [<<Null>>,<<NewInstance>>] /// CHECK: Throw /// CHECK-START: void Main.testPhiInput() code_sinking (after) /// CHECK: <<Null:l\d+>> NullConstant /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:java.lang.Object /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: begin_block /// CHECK: Phi [<<Null>>,<<NewInstance>>] /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw public static void testPhiInput() { Object f = new Object(); if (doThrow) { Object o = null; int i = 2; if (doLoop) { o = f; i = 42; } throw new Error(o.toString() + i); } } static void $opt$noinline$foo() {} // Check that we do not move volatile stores. /// CHECK-START: void Main.testVolatileStore() code_sinking (before) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testVolatileStore() code_sinking (after) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw public static void testVolatileStore() { Main m = new Main(); m.volatileField = 42; if (doThrow) { throw new Error(m.toString()); } } public static void testInstanceSideEffects() { int a = mainField.intField; $noinline$changeIntField(); if (doThrow) { throw new Error("" + a); } } static void $noinline$changeIntField() { mainField.intField = 42; } public static void testStaticSideEffects() { Object o = obj; $noinline$changeStaticObjectField(); if (doThrow) { throw new Error(o.getClass().toString()); } } static void $noinline$changeStaticObjectField() { obj = new Main(); } // Test that we preserve the order of stores. /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK: <<Int43:i\d+>> IntConstant 43 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int43>>] /// CHECK: If /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK: <<Int43:i\d+>> IntConstant 43 /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK-NOT: begin_block /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK-NOT: begin_block /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK-NOT: begin_block /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int43>>] /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw public static void testStoreStore(boolean doThrow) { Main m = new Main(); m.intField = 42; m.intField = 43; if (doThrow) { throw new Error(m.$opt$noinline$toString()); } } public String $opt$noinline$toString() { return "" + intField; } volatile int volatileField; int intField; Object objectField; static boolean doThrow; static boolean doLoop; static Main mainField = new Main(); static Object obj = new Object(); }