package org.graalvm.compiler.core.test.ea;
import java.util.List;
import org.graalvm.compiler.graph.Node;
import org.graalvm.compiler.loop.DefaultLoopPolicies;
import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
import org.graalvm.compiler.loop.phases.LoopPeelingPhase;
import org.graalvm.compiler.nodes.ConstantNode;
import org.graalvm.compiler.nodes.ReturnNode;
import org.graalvm.compiler.nodes.extended.BoxNode;
import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
import org.graalvm.compiler.nodes.java.LoadFieldNode;
import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
import org.graalvm.compiler.phases.common.CanonicalizerPhase;
import org.graalvm.compiler.phases.schedule.SchedulePhase;
import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Test;
import jdk.vm.ci.meta.JavaConstant;
public class EscapeAnalysisTest extends EATestBase {
@Test
public void test1() {
testEscapeAnalysis("test1Snippet", JavaConstant.forInt(101), false);
}
public static int test1Snippet() {
Integer x = new Integer(101);
return x.intValue();
}
@Test
public void test2() {
testEscapeAnalysis("test2Snippet", JavaConstant.forInt(0), false);
}
public static int test2Snippet() {
Integer[] x = new Integer[0];
return x.length;
}
@Test
public void test3() {
testEscapeAnalysis("test3Snippet", JavaConstant.NULL_POINTER, false);
}
public static Object test3Snippet() {
Integer[] x = new Integer[1];
return x[0];
}
@Test
public void testMonitor() {
testEscapeAnalysis("testMonitorSnippet", JavaConstant.forInt(0), false);
}
public static int testMonitorSnippet() {
Integer x = new Integer(0);
Double y = new Double(0);
Object z = new Object();
synchronized (x) {
synchronized (y) {
synchronized (z) {
notInlineable();
}
}
}
return x.intValue();
}
@Test
public void testMonitor2() {
testEscapeAnalysis("testMonitor2Snippet", JavaConstant.forInt(0), false);
}
public static int testMonitor2Snippet() {
Integer x = new Integer(0);
Double y = new Double(0);
Object z = new Object();
synchronized (x) {
synchronized (y) {
synchronized (z) {
notInlineable();
return x.intValue();
}
}
}
}
@Test
public void testMerge() {
testEscapeAnalysis("testMerge1Snippet", JavaConstant.forInt(0), true);
}
public static int testMerge1Snippet(int a) {
TestClassInt obj = new TestClassInt(1, 0);
if (a < 0) {
obj.x = obj.x + 1;
} else {
obj.x = obj.x + 2;
obj.y = 0;
}
if (obj.x > 1000) {
return 1;
}
return obj.y;
}
@Test
public void testSimpleLoop() {
testEscapeAnalysis("testSimpleLoopSnippet", JavaConstant.forInt(1), false);
}
public int testSimpleLoopSnippet(int a) {
TestClassInt obj = new TestClassInt(1, 2);
for (int i = 0; i < a; i++) {
notInlineable();
}
return obj.x;
}
@Test
public void testModifyingLoop() {
testEscapeAnalysis("testModifyingLoopSnippet", JavaConstant.forInt(1), false);
}
public int testModifyingLoopSnippet(int a) {
TestClassInt obj = new TestClassInt(1, 2);
for (int i = 0; i < a; i++) {
obj.x = 3;
notInlineable();
}
return obj.x <= 3 ? 1 : 0;
}
@Test
public void testMergeAllocationsInt() {
testEscapeAnalysis("testMergeAllocationsIntSnippet", JavaConstant.forInt(1), false);
}
public int testMergeAllocationsIntSnippet(int a) {
TestClassInt obj;
if (a < 0) {
obj = new TestClassInt(1, 2);
notInlineable();
} else {
obj = new TestClassInt(1, 2);
notInlineable();
}
return obj.x <= 3 ? 1 : 0;
}
@Test
public void testMergeAllocationsInt2() {
testEscapeAnalysis("testMergeAllocationsInt2Snippet", JavaConstant.forInt(1), true);
}
public int testMergeAllocationsInt2Snippet(int a) {
TestClassInt obj = new TestClassInt(1, 2);
if (a < 0) {
notInlineable();
} else {
obj = new TestClassInt(1, 2);
notInlineable();
}
return obj.x <= 3 ? 1 : 0;
}
@Test
public void testMergeAllocationsInt3() {
assertTrue(testMergeAllocationsInt3Snippet(true));
assertFalse(testMergeAllocationsInt3Snippet(false));
prepareGraph("testMergeAllocationsInt3Snippet", true);
assertFalse(graph.getNodes().filter(ReturnNode.class).first().result().isConstant());
}
public boolean testMergeAllocationsInt3Snippet(boolean a) {
TestClassInt phi1;
TestClassInt phi2;
if (a) {
field = new TestClassObject();
field = new TestClassObject();
phi1 = phi2 = new TestClassInt(1, 2);
} else {
phi1 = new TestClassInt(2, 3);
phi2 = new TestClassInt(3, 4);
}
return phi1 == phi2;
}
@Test
public void testMergeAllocationsObj() {
testEscapeAnalysis("testMergeAllocationsObjSnippet", JavaConstant.forInt(1), false);
}
public int testMergeAllocationsObjSnippet(int a) {
TestClassObject obj;
Integer one = 1;
Integer two = 2;
Integer three = 3;
if (a < 0) {
obj = new TestClassObject(one, two);
notInlineable();
} else {
obj = new TestClassObject(one, three);
notInlineable();
}
return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
}
@Test
public void testMergeAllocationsObjCirc() {
testEscapeAnalysis("testMergeAllocationsObjCircSnippet", JavaConstant.forInt(1), false);
}
public int testMergeAllocationsObjCircSnippet(int a) {
TestClassObject obj;
Integer one = 1;
Integer two = 2;
Integer three = 3;
if (a < 0) {
obj = new TestClassObject(one);
obj.y = obj;
obj.y = two;
notInlineable();
} else {
obj = new TestClassObject(one);
obj.y = obj;
obj.y = three;
notInlineable();
}
return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
}
static class MyException extends RuntimeException {
private static final long serialVersionUID = 0L;
protected Integer value;
MyException(Integer value) {
super((Throwable) null);
this.value = value;
}
@SuppressWarnings("sync-override")
@Override
public final Throwable fillInStackTrace() {
return null;
}
}
@Test
public void testMergeAllocationsException() {
testEscapeAnalysis("testMergeAllocationsExceptionSnippet", JavaConstant.forInt(1), false);
}
public int testMergeAllocationsExceptionSnippet(int a) {
MyException obj;
Integer one = 1;
if (a < 0) {
obj = new MyException(one);
notInlineable();
} else {
obj = new MyException(one);
notInlineable();
}
return obj.value <= 3 ? 1 : 0;
}
@Test
public void testChangeHandling() {
prepareGraph("testChangeHandlingSnippet", false);
Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count());
Assert.assertEquals(1, graph.getNodes().filter(BoxNode.class).count());
List<Node> nodes = graph.getNodes().snapshot();
new PartialEscapePhase(false, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
Assert.assertEquals(nodes.size(), graph.getNodeCount());
for (Node node : nodes) {
Assert.assertTrue(node.isAlive());
}
}
public volatile Object field;
public int testChangeHandlingSnippet(int a) {
Object obj;
Integer one = 1;
obj = new MyException(one);
if (a < 0) {
notInlineable();
} else {
obj = new Integer(1);
notInlineable();
}
field = obj;
return 1;
}
@Test
public void testRemovalSpecialCase() {
prepareGraph("testRemovalSpecialCaseSnippet", false);
Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count());
graph.replaceFixedWithFloating(graph.getNodes().filter(LoadFieldNode.class).first(), graph.unique(ConstantNode.forInt(0)));
new CanonicalizerPhase().apply(graph, context);
new PartialEscapePhase(false, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
Assert.assertEquals(0, graph.getNodes().filter(CommitAllocationNode.class).count());
}
public volatile int field2;
public int testRemovalSpecialCaseSnippet(int a) {
Object phi = new Object();
for (int i = 0; i < a; i++) {
field = null;
if (field2 == 1) {
phi = new Object();
}
}
return phi == null ? 1 : 0;
}
@Test
public void testCheckCast() {
testEscapeAnalysis("testCheckCastSnippet", getSnippetReflection().forObject(TestClassObject.class), true);
}
public Object testCheckCastSnippet() {
TestClassObject obj = new TestClassObject(TestClassObject.class);
TestClassObject obj2 = new TestClassObject(obj);
return ((TestClassObject) obj2.x).x;
}
@Test
public void testInstanceOf() {
testEscapeAnalysis("testInstanceOfSnippet", JavaConstant.forInt(1), false);
}
public boolean testInstanceOfSnippet() {
TestClassObject obj = new TestClassObject(TestClassObject.class);
TestClassObject obj2 = new TestClassObject(obj);
return obj2.x instanceof TestClassObject;
}
@SuppressWarnings("unused")
public static void testNewNodeSnippet() {
new ValueAnchorNode(null);
}
@Test
public void testNewNode() {
Assume.assumeFalse(Node.TRACK_CREATION_POSITION);
testEscapeAnalysis("testNewNodeSnippet", null, false);
}
private static final TestClassObject staticObj = new TestClassObject();
public static Object testFullyUnrolledLoopSnippet() {
TestClassObject obj = staticObj;
for (int i = 0; i < 2; i++) {
obj = new TestClassObject(obj);
}
return obj.x;
}
@Test
public void testFullyUnrolledLoop() {
prepareGraph("testFullyUnrolledLoopSnippet", false);
new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
new PartialEscapePhase(false, new CanonicalizerPhase(), graph.getOptions()).apply(graph, context);
Assert.assertEquals(1, returnNodes.size());
Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
Assert.assertEquals(2, commit.getValues().size());
Assert.assertEquals(1, commit.getVirtualObjects().size());
Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
}
@SuppressWarnings("unused") private static Object staticField;
private static TestClassObject inlinedPart(TestClassObject obj) {
TestClassObject ret = new TestClassObject(obj);
staticField = null;
return ret;
}
public static Object testPeeledLoopSnippet() {
TestClassObject obj = staticObj;
int i = 0;
do {
obj = inlinedPart(obj);
} while (i++ < 10);
staticField = obj;
return obj.x;
}
@Test
public void testPeeledLoop() {
prepareGraph("testPeeledLoopSnippet", false);
new LoopPeelingPhase(new DefaultLoopPolicies()).apply(graph, getDefaultHighTierContext());
new SchedulePhase(graph.getOptions()).apply(graph);
}
public static void testDeoptMonitorSnippetInner(Object o2, Object t, int i) {
staticField = null;
if (i == 0) {
staticField = o2;
Number n = (Number) t;
n.toString();
}
}
public static void testDeoptMonitorSnippet(Object t, int i) {
TestClassObject o = new TestClassObject();
TestClassObject o2 = new TestClassObject(o);
synchronized (o) {
testDeoptMonitorSnippetInner(o2, t, i);
}
}
@Test
public void testDeoptMonitor() {
test("testDeoptMonitorSnippet", new Object(), 0);
}
}