357 lines
13 KiB
Java
357 lines
13 KiB
Java
/*
|
|
* 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: <<New:l\d+>> NewInstance [<<LoadClass>>]
|
|
/// CHECK: ConstructorFence [<<New>>]
|
|
/// 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: <<New:l\d+>> NewInstance [<<LoadClass>>]
|
|
/// CHECK: ConstructorFence [<<New>>]
|
|
/// 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();
|
|
}
|