1 /*
   2  * Copyright (c) 2011, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.graalvm.compiler.core.test.ea;
  24 
  25 import jdk.vm.ci.meta.JavaConstant;
  26 
  27 import org.junit.Assert;
  28 import org.junit.Test;
  29 
  30 import org.graalvm.compiler.graph.Node;
  31 import org.graalvm.compiler.loop.DefaultLoopPolicies;
  32 import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
  33 import org.graalvm.compiler.loop.phases.LoopPeelingPhase;
  34 import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
  35 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
  36 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
  37 import org.graalvm.compiler.phases.common.CanonicalizerPhase;
  38 import org.graalvm.compiler.phases.schedule.SchedulePhase;
  39 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
  40 
  41 /**
  42  * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
  43  * values.
  44  */
  45 public class EscapeAnalysisTest extends EATestBase {
  46 
  47     @Test
  48     public void test1() {
  49         testEscapeAnalysis("test1Snippet", JavaConstant.forInt(101), false);
  50     }
  51 
  52     public static int test1Snippet() {
  53         Integer x = new Integer(101);
  54         return x.intValue();
  55     }
  56 
  57     @Test
  58     public void test2() {
  59         testEscapeAnalysis("test2Snippet", JavaConstant.forInt(0), false);
  60     }
  61 
  62     public static int test2Snippet() {
  63         Integer[] x = new Integer[0];
  64         return x.length;
  65     }
  66 
  67     @Test
  68     public void test3() {
  69         testEscapeAnalysis("test3Snippet", JavaConstant.NULL_POINTER, false);
  70     }
  71 
  72     public static Object test3Snippet() {
  73         Integer[] x = new Integer[1];
  74         return x[0];
  75     }
  76 
  77     @Test
  78     public void testMonitor() {
  79         testEscapeAnalysis("testMonitorSnippet", JavaConstant.forInt(0), false);
  80     }
  81 
  82     public static int testMonitorSnippet() {
  83         Integer x = new Integer(0);
  84         Double y = new Double(0);
  85         Object z = new Object();
  86         synchronized (x) {
  87             synchronized (y) {
  88                 synchronized (z) {
  89                     notInlineable();
  90                 }
  91             }
  92         }
  93         return x.intValue();
  94     }
  95 
  96     @Test
  97     public void testMonitor2() {
  98         testEscapeAnalysis("testMonitor2Snippet", JavaConstant.forInt(0), false);
  99     }
 100 
 101     /**
 102      * This test case differs from the last one in that it requires inlining within a synchronized
 103      * region.
 104      */
 105     public static int testMonitor2Snippet() {
 106         Integer x = new Integer(0);
 107         Double y = new Double(0);
 108         Object z = new Object();
 109         synchronized (x) {
 110             synchronized (y) {
 111                 synchronized (z) {
 112                     notInlineable();
 113                     return x.intValue();
 114                 }
 115             }
 116         }
 117     }
 118 
 119     @Test
 120     public void testMerge() {
 121         testEscapeAnalysis("testMerge1Snippet", JavaConstant.forInt(0), true);
 122     }
 123 
 124     public static int testMerge1Snippet(int a) {
 125         TestClassInt obj = new TestClassInt(1, 0);
 126         if (a < 0) {
 127             obj.x = obj.x + 1;
 128         } else {
 129             obj.x = obj.x + 2;
 130             obj.y = 0;
 131         }
 132         if (obj.x > 1000) {
 133             return 1;
 134         }
 135         return obj.y;
 136     }
 137 
 138     @Test
 139     public void testSimpleLoop() {
 140         testEscapeAnalysis("testSimpleLoopSnippet", JavaConstant.forInt(1), false);
 141     }
 142 
 143     public int testSimpleLoopSnippet(int a) {
 144         TestClassInt obj = new TestClassInt(1, 2);
 145         for (int i = 0; i < a; i++) {
 146             notInlineable();
 147         }
 148         return obj.x;
 149     }
 150 
 151     @Test
 152     public void testModifyingLoop() {
 153         testEscapeAnalysis("testModifyingLoopSnippet", JavaConstant.forInt(1), false);
 154     }
 155 
 156     public int testModifyingLoopSnippet(int a) {
 157         TestClassInt obj = new TestClassInt(1, 2);
 158         for (int i = 0; i < a; i++) {
 159             obj.x = 3;
 160             notInlineable();
 161         }
 162         return obj.x <= 3 ? 1 : 0;
 163     }
 164 
 165     @Test
 166     public void testMergeAllocationsInt() {
 167         testEscapeAnalysis("testMergeAllocationsIntSnippet", JavaConstant.forInt(1), false);
 168     }
 169 
 170     public int testMergeAllocationsIntSnippet(int a) {
 171         TestClassInt obj;
 172         if (a < 0) {
 173             obj = new TestClassInt(1, 2);
 174             notInlineable();
 175         } else {
 176             obj = new TestClassInt(1, 2);
 177             notInlineable();
 178         }
 179         return obj.x <= 3 ? 1 : 0;
 180     }
 181 
 182     @Test
 183     public void testMergeAllocationsObj() {
 184         testEscapeAnalysis("testMergeAllocationsObjSnippet", JavaConstant.forInt(1), false);
 185     }
 186 
 187     public int testMergeAllocationsObjSnippet(int a) {
 188         TestClassObject obj;
 189         Integer one = 1;
 190         Integer two = 2;
 191         Integer three = 3;
 192         if (a < 0) {
 193             obj = new TestClassObject(one, two);
 194             notInlineable();
 195         } else {
 196             obj = new TestClassObject(one, three);
 197             notInlineable();
 198         }
 199         return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
 200     }
 201 
 202     @Test
 203     public void testMergeAllocationsObjCirc() {
 204         testEscapeAnalysis("testMergeAllocationsObjCircSnippet", JavaConstant.forInt(1), false);
 205     }
 206 
 207     public int testMergeAllocationsObjCircSnippet(int a) {
 208         TestClassObject obj;
 209         Integer one = 1;
 210         Integer two = 2;
 211         Integer three = 3;
 212         if (a < 0) {
 213             obj = new TestClassObject(one);
 214             obj.y = obj;
 215             obj.y = two;
 216             notInlineable();
 217         } else {
 218             obj = new TestClassObject(one);
 219             obj.y = obj;
 220             obj.y = three;
 221             notInlineable();
 222         }
 223         return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
 224     }
 225 
 226     static class MyException extends RuntimeException {
 227 
 228         private static final long serialVersionUID = 0L;
 229 
 230         protected Integer value;
 231 
 232         MyException(Integer value) {
 233             super((Throwable) null);
 234             this.value = value;
 235         }
 236 
 237         @SuppressWarnings("sync-override")
 238         @Override
 239         public final Throwable fillInStackTrace() {
 240             return null;
 241         }
 242     }
 243 
 244     @Test
 245     public void testMergeAllocationsException() {
 246         testEscapeAnalysis("testMergeAllocationsExceptionSnippet", JavaConstant.forInt(1), false);
 247     }
 248 
 249     public int testMergeAllocationsExceptionSnippet(int a) {
 250         MyException obj;
 251         Integer one = 1;
 252         if (a < 0) {
 253             obj = new MyException(one);
 254             notInlineable();
 255         } else {
 256             obj = new MyException(one);
 257             notInlineable();
 258         }
 259         return obj.value <= 3 ? 1 : 0;
 260     }
 261 
 262     @Test
 263     public void testCheckCast() {
 264         testEscapeAnalysis("testCheckCastSnippet", getSnippetReflection().forObject(TestClassObject.class), false);
 265     }
 266 
 267     public Object testCheckCastSnippet() {
 268         TestClassObject obj = new TestClassObject(TestClassObject.class);
 269         TestClassObject obj2 = new TestClassObject(obj);
 270         return ((TestClassObject) obj2.x).x;
 271     }
 272 
 273     @Test
 274     public void testInstanceOf() {
 275         testEscapeAnalysis("testInstanceOfSnippet", JavaConstant.forInt(1), false);
 276     }
 277 
 278     public boolean testInstanceOfSnippet() {
 279         TestClassObject obj = new TestClassObject(TestClassObject.class);
 280         TestClassObject obj2 = new TestClassObject(obj);
 281         return obj2.x instanceof TestClassObject;
 282     }
 283 
 284     @SuppressWarnings("unused")
 285     public static void testNewNodeSnippet() {
 286         new ValueAnchorNode(null);
 287     }
 288 
 289     /**
 290      * This test makes sure that the allocation of a {@link Node} can be removed. It therefore also
 291      * tests the intrinsification of {@link Object#getClass()}.
 292      */
 293     @Test
 294     public void testNewNode() {
 295         testEscapeAnalysis("testNewNodeSnippet", null, false);
 296     }
 297 
 298     private static final TestClassObject staticObj = new TestClassObject();
 299 
 300     public static Object testFullyUnrolledLoopSnippet() {
 301         /*
 302          * This tests a case that can appear if PEA is performed both before and after loop
 303          * unrolling/peeling: If the VirtualInstanceNode is not duplicated correctly with the loop,
 304          * the resulting object will reference itself, and not a second (different) object.
 305          */
 306         TestClassObject obj = staticObj;
 307         for (int i = 0; i < 2; i++) {
 308             obj = new TestClassObject(obj);
 309         }
 310         return obj.x;
 311     }
 312 
 313     @Test
 314     public void testFullyUnrolledLoop() {
 315         prepareGraph("testFullyUnrolledLoopSnippet", false);
 316         new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
 317         new PartialEscapePhase(false, new CanonicalizerPhase()).apply(graph, context);
 318         Assert.assertEquals(1, returnNodes.size());
 319         Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
 320         CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
 321         Assert.assertEquals(2, commit.getValues().size());
 322         Assert.assertEquals(1, commit.getVirtualObjects().size());
 323         Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
 324     }
 325 
 326     @SuppressWarnings("unused") private static Object staticField;
 327 
 328     private static TestClassObject inlinedPart(TestClassObject obj) {
 329         TestClassObject ret = new TestClassObject(obj);
 330         staticField = null;
 331         return ret;
 332     }
 333 
 334     public static Object testPeeledLoopSnippet() {
 335         TestClassObject obj = staticObj;
 336         int i = 0;
 337         do {
 338             obj = inlinedPart(obj);
 339         } while (i++ < 10);
 340         staticField = obj;
 341         return obj.x;
 342     }
 343 
 344     @Test
 345     public void testPeeledLoop() {
 346         prepareGraph("testPeeledLoopSnippet", false);
 347         new LoopPeelingPhase(new DefaultLoopPolicies()).apply(graph, getDefaultHighTierContext());
 348         new SchedulePhase().apply(graph);
 349     }
 350 
 351     public static void testDeoptMonitorSnippetInner(Object o2, Object t, int i) {
 352         staticField = null;
 353         if (i == 0) {
 354             staticField = o2;
 355             Number n = (Number) t;
 356             n.toString();
 357         }
 358     }
 359 
 360     public static void testDeoptMonitorSnippet(Object t, int i) {
 361         TestClassObject o = new TestClassObject();
 362         TestClassObject o2 = new TestClassObject(o);
 363 
 364         synchronized (o) {
 365             testDeoptMonitorSnippetInner(o2, t, i);
 366         }
 367     }
 368 
 369     @Test
 370     public void testDeoptMonitor() {
 371         test("testDeoptMonitorSnippet", new Object(), 0);
 372     }
 373 }