1 /* 2 * Copyright (c) 2011, 2017, 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 java.util.List; 26 27 import org.graalvm.compiler.graph.Node; 28 import org.graalvm.compiler.loop.DefaultLoopPolicies; 29 import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase; 30 import org.graalvm.compiler.loop.phases.LoopPeelingPhase; 31 import org.graalvm.compiler.nodes.ConstantNode; 32 import org.graalvm.compiler.nodes.ReturnNode; 33 import org.graalvm.compiler.nodes.extended.BoxNode; 34 import org.graalvm.compiler.nodes.extended.ValueAnchorNode; 35 import org.graalvm.compiler.nodes.java.LoadFieldNode; 36 import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode; 37 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode; 38 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 39 import org.graalvm.compiler.phases.schedule.SchedulePhase; 40 import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase; 41 import org.junit.Assert; 42 import org.junit.Test; 43 44 import jdk.vm.ci.meta.JavaConstant; 45 46 /** 47 * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct 48 * values. 49 */ 50 public class EscapeAnalysisTest extends EATestBase { 51 52 @Test 53 public void test1() { 54 testEscapeAnalysis("test1Snippet", JavaConstant.forInt(101), false); 55 } 56 57 public static int test1Snippet() { 58 Integer x = new Integer(101); 59 return x.intValue(); 60 } 61 62 @Test 63 public void test2() { 64 testEscapeAnalysis("test2Snippet", JavaConstant.forInt(0), false); 65 } 66 67 public static int test2Snippet() { 68 Integer[] x = new Integer[0]; 69 return x.length; 70 } 71 72 @Test 73 public void test3() { 74 testEscapeAnalysis("test3Snippet", JavaConstant.NULL_POINTER, false); 75 } 76 77 public static Object test3Snippet() { 78 Integer[] x = new Integer[1]; 79 return x[0]; 80 } 81 82 @Test 83 public void testMonitor() { 84 testEscapeAnalysis("testMonitorSnippet", JavaConstant.forInt(0), false); 85 } 86 87 public static int testMonitorSnippet() { 88 Integer x = new Integer(0); 89 Double y = new Double(0); 90 Object z = new Object(); 91 synchronized (x) { 92 synchronized (y) { 93 synchronized (z) { 94 notInlineable(); 95 } 96 } 97 } 98 return x.intValue(); 99 } 100 101 @Test 102 public void testMonitor2() { 103 testEscapeAnalysis("testMonitor2Snippet", JavaConstant.forInt(0), false); 104 } 105 106 /** 107 * This test case differs from the last one in that it requires inlining within a synchronized 108 * region. 109 */ 110 public static int testMonitor2Snippet() { 111 Integer x = new Integer(0); 112 Double y = new Double(0); 113 Object z = new Object(); 114 synchronized (x) { 115 synchronized (y) { 116 synchronized (z) { 117 notInlineable(); 118 return x.intValue(); 119 } 120 } 121 } 122 } 123 124 @Test 125 public void testMerge() { 126 testEscapeAnalysis("testMerge1Snippet", JavaConstant.forInt(0), true); 127 } 128 129 public static int testMerge1Snippet(int a) { 130 TestClassInt obj = new TestClassInt(1, 0); 131 if (a < 0) { 132 obj.x = obj.x + 1; 133 } else { 134 obj.x = obj.x + 2; 135 obj.y = 0; 136 } 137 if (obj.x > 1000) { 138 return 1; 139 } 140 return obj.y; 141 } 142 143 @Test 144 public void testSimpleLoop() { 145 testEscapeAnalysis("testSimpleLoopSnippet", JavaConstant.forInt(1), false); 146 } 147 148 public int testSimpleLoopSnippet(int a) { 149 TestClassInt obj = new TestClassInt(1, 2); 150 for (int i = 0; i < a; i++) { 151 notInlineable(); 152 } 153 return obj.x; 154 } 155 156 @Test 157 public void testModifyingLoop() { 158 testEscapeAnalysis("testModifyingLoopSnippet", JavaConstant.forInt(1), false); 159 } 160 161 public int testModifyingLoopSnippet(int a) { 162 TestClassInt obj = new TestClassInt(1, 2); 163 for (int i = 0; i < a; i++) { 164 obj.x = 3; 165 notInlineable(); 166 } 167 return obj.x <= 3 ? 1 : 0; 168 } 169 170 @Test 171 public void testMergeAllocationsInt() { 172 testEscapeAnalysis("testMergeAllocationsIntSnippet", JavaConstant.forInt(1), false); 173 } 174 175 public int testMergeAllocationsIntSnippet(int a) { 176 TestClassInt obj; 177 if (a < 0) { 178 obj = new TestClassInt(1, 2); 179 notInlineable(); 180 } else { 181 obj = new TestClassInt(1, 2); 182 notInlineable(); 183 } 184 return obj.x <= 3 ? 1 : 0; 185 } 186 187 @Test 188 public void testMergeAllocationsInt2() { 189 testEscapeAnalysis("testMergeAllocationsInt2Snippet", JavaConstant.forInt(1), true); 190 } 191 192 public int testMergeAllocationsInt2Snippet(int a) { 193 /* 194 * The initial object in obj exists until the end of the function, but it can still be 195 * merged with the one allocated in the else block because noone can observe the identity. 196 */ 197 TestClassInt obj = new TestClassInt(1, 2); 198 if (a < 0) { 199 notInlineable(); 200 } else { 201 obj = new TestClassInt(1, 2); 202 notInlineable(); 203 } 204 return obj.x <= 3 ? 1 : 0; 205 } 206 207 @Test 208 public void testMergeAllocationsInt3() { 209 // ensure that the result is not constant: 210 assertTrue(testMergeAllocationsInt3Snippet(true)); 211 assertFalse(testMergeAllocationsInt3Snippet(false)); 212 213 prepareGraph("testMergeAllocationsInt3Snippet", true); 214 assertFalse(graph.getNodes().filter(ReturnNode.class).first().result().isConstant()); 215 } 216 217 public boolean testMergeAllocationsInt3Snippet(boolean a) { 218 TestClassInt phi1; 219 TestClassInt phi2; 220 if (a) { 221 field = new TestClassObject(); 222 field = new TestClassObject(); 223 phi1 = phi2 = new TestClassInt(1, 2); 224 } else { 225 phi1 = new TestClassInt(2, 3); 226 phi2 = new TestClassInt(3, 4); 227 } 228 return phi1 == phi2; 229 } 230 231 @Test 232 public void testMergeAllocationsObj() { 233 testEscapeAnalysis("testMergeAllocationsObjSnippet", JavaConstant.forInt(1), false); 234 } 235 236 public int testMergeAllocationsObjSnippet(int a) { 237 TestClassObject obj; 238 Integer one = 1; 239 Integer two = 2; 240 Integer three = 3; 241 if (a < 0) { 242 obj = new TestClassObject(one, two); 243 notInlineable(); 244 } else { 245 obj = new TestClassObject(one, three); 246 notInlineable(); 247 } 248 return ((Integer) obj.x).intValue() <= 3 ? 1 : 0; 249 } 250 251 @Test 252 public void testMergeAllocationsObjCirc() { 253 testEscapeAnalysis("testMergeAllocationsObjCircSnippet", JavaConstant.forInt(1), false); 254 } 255 256 public int testMergeAllocationsObjCircSnippet(int a) { 257 TestClassObject obj; 258 Integer one = 1; 259 Integer two = 2; 260 Integer three = 3; 261 if (a < 0) { 262 obj = new TestClassObject(one); 263 obj.y = obj; 264 obj.y = two; 265 notInlineable(); 266 } else { 267 obj = new TestClassObject(one); 268 obj.y = obj; 269 obj.y = three; 270 notInlineable(); 271 } 272 return ((Integer) obj.x).intValue() <= 3 ? 1 : 0; 273 } 274 275 static class MyException extends RuntimeException { 276 277 private static final long serialVersionUID = 0L; 278 279 protected Integer value; 280 281 MyException(Integer value) { 282 super((Throwable) null); 283 this.value = value; 284 } 285 286 @SuppressWarnings("sync-override") 287 @Override 288 public final Throwable fillInStackTrace() { 289 return null; 290 } 291 } 292 293 @Test 294 public void testMergeAllocationsException() { 295 testEscapeAnalysis("testMergeAllocationsExceptionSnippet", JavaConstant.forInt(1), false); 296 } 297 298 public int testMergeAllocationsExceptionSnippet(int a) { 299 MyException obj; 300 Integer one = 1; 301 if (a < 0) { 302 obj = new MyException(one); 303 notInlineable(); 304 } else { 305 obj = new MyException(one); 306 notInlineable(); 307 } 308 return obj.value <= 3 ? 1 : 0; 309 } 310 311 /** 312 * Tests that a graph with allocations that does not make progress during PEA will not be 313 * changed. 314 */ 315 @Test 316 public void testChangeHandling() { 317 prepareGraph("testChangeHandlingSnippet", false); 318 Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count()); 319 Assert.assertEquals(1, graph.getNodes().filter(BoxNode.class).count()); 320 List<Node> nodes = graph.getNodes().snapshot(); 321 // verify that an additional run doesn't add or remove nodes 322 new PartialEscapePhase(false, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context); 323 Assert.assertEquals(nodes.size(), graph.getNodeCount()); 324 for (Node node : nodes) { 325 Assert.assertTrue(node.isAlive()); 326 } 327 } 328 329 public volatile Object field; 330 331 public int testChangeHandlingSnippet(int a) { 332 Object obj; 333 Integer one = 1; 334 obj = new MyException(one); 335 if (a < 0) { 336 notInlineable(); 337 } else { 338 obj = new Integer(1); 339 notInlineable(); 340 } 341 field = obj; 342 return 1; 343 } 344 345 /** 346 * Test the case where allocations before and during a loop that have no usages other than their 347 * phi need to be recognized as an important change. This needs a loop so that the allocation is 348 * not trivially removed by dead code elimination. 349 */ 350 @Test 351 public void testRemovalSpecialCase() { 352 prepareGraph("testRemovalSpecialCaseSnippet", false); 353 Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count()); 354 // create the situation by removing the if 355 graph.replaceFixedWithFloating(graph.getNodes().filter(LoadFieldNode.class).first(), graph.unique(ConstantNode.forInt(0))); 356 new CanonicalizerPhase().apply(graph, context); 357 // verify that an additional run removes all allocations 358 new PartialEscapePhase(false, false, new CanonicalizerPhase(), null, graph.getOptions()).apply(graph, context); 359 Assert.assertEquals(0, graph.getNodes().filter(CommitAllocationNode.class).count()); 360 } 361 362 public volatile int field2; 363 364 public int testRemovalSpecialCaseSnippet(int a) { 365 Object phi = new Object(); 366 for (int i = 0; i < a; i++) { 367 field = null; 368 if (field2 == 1) { 369 phi = new Object(); 370 } 371 } 372 return phi == null ? 1 : 0; 373 } 374 375 @Test 376 public void testCheckCast() { 377 testEscapeAnalysis("testCheckCastSnippet", getSnippetReflection().forObject(TestClassObject.class), true); 378 } 379 380 public Object testCheckCastSnippet() { 381 TestClassObject obj = new TestClassObject(TestClassObject.class); 382 TestClassObject obj2 = new TestClassObject(obj); 383 return ((TestClassObject) obj2.x).x; 384 } 385 386 @Test 387 public void testInstanceOf() { 388 testEscapeAnalysis("testInstanceOfSnippet", JavaConstant.forInt(1), false); 389 } 390 391 public boolean testInstanceOfSnippet() { 392 TestClassObject obj = new TestClassObject(TestClassObject.class); 393 TestClassObject obj2 = new TestClassObject(obj); 394 return obj2.x instanceof TestClassObject; 395 } 396 397 @SuppressWarnings("unused") 398 public static void testNewNodeSnippet() { 399 new ValueAnchorNode(null); 400 } 401 402 /** 403 * This test makes sure that the allocation of a {@link Node} can be removed. It therefore also 404 * tests the intrinsification of {@link Object#getClass()}. 405 */ 406 @Test 407 public void testNewNode() { 408 testEscapeAnalysis("testNewNodeSnippet", null, false); 409 } 410 411 private static final TestClassObject staticObj = new TestClassObject(); 412 413 public static Object testFullyUnrolledLoopSnippet() { 414 /* 415 * This tests a case that can appear if PEA is performed both before and after loop 416 * unrolling/peeling: If the VirtualInstanceNode is not duplicated correctly with the loop, 417 * the resulting object will reference itself, and not a second (different) object. 418 */ 419 TestClassObject obj = staticObj; 420 for (int i = 0; i < 2; i++) { 421 obj = new TestClassObject(obj); 422 } 423 return obj.x; 424 } 425 426 @Test 427 public void testFullyUnrolledLoop() { 428 prepareGraph("testFullyUnrolledLoopSnippet", false); 429 new LoopFullUnrollPhase(new CanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context); 430 new PartialEscapePhase(false, new CanonicalizerPhase(), graph.getOptions()).apply(graph, context); 431 Assert.assertEquals(1, returnNodes.size()); 432 Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode); 433 CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit(); 434 Assert.assertEquals(2, commit.getValues().size()); 435 Assert.assertEquals(1, commit.getVirtualObjects().size()); 436 Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0)); 437 } 438 439 @SuppressWarnings("unused") private static Object staticField; 440 441 private static TestClassObject inlinedPart(TestClassObject obj) { 442 TestClassObject ret = new TestClassObject(obj); 443 staticField = null; 444 return ret; 445 } 446 447 public static Object testPeeledLoopSnippet() { 448 TestClassObject obj = staticObj; 449 int i = 0; 450 do { 451 obj = inlinedPart(obj); 452 } while (i++ < 10); 453 staticField = obj; 454 return obj.x; 455 } 456 457 @Test 458 public void testPeeledLoop() { 459 prepareGraph("testPeeledLoopSnippet", false); 460 new LoopPeelingPhase(new DefaultLoopPolicies()).apply(graph, getDefaultHighTierContext()); 461 new SchedulePhase(graph.getOptions()).apply(graph); 462 } 463 464 public static void testDeoptMonitorSnippetInner(Object o2, Object t, int i) { 465 staticField = null; 466 if (i == 0) { 467 staticField = o2; 468 Number n = (Number) t; 469 n.toString(); 470 } 471 } 472 473 public static void testDeoptMonitorSnippet(Object t, int i) { 474 TestClassObject o = new TestClassObject(); 475 TestClassObject o2 = new TestClassObject(o); 476 477 synchronized (o) { 478 testDeoptMonitorSnippetInner(o2, t, i); 479 } 480 } 481 482 @Test 483 public void testDeoptMonitor() { 484 test("testDeoptMonitorSnippet", new Object(), 0); 485 } 486 }