1 /* 2 * Copyright (c) 2011, 2012, 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.lang.ref.SoftReference; 26 27 import org.junit.Assert; 28 import org.junit.Ignore; 29 import org.junit.Test; 30 31 import org.graalvm.compiler.api.directives.GraalDirectives; 32 import org.graalvm.compiler.core.test.TypeSystemTest; 33 import org.graalvm.compiler.graph.Node; 34 import org.graalvm.compiler.nodes.AbstractMergeNode; 35 import org.graalvm.compiler.nodes.ReturnNode; 36 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 37 import org.graalvm.compiler.nodes.extended.BoxNode; 38 import org.graalvm.compiler.nodes.extended.UnboxNode; 39 import org.graalvm.compiler.nodes.java.LoadFieldNode; 40 import org.graalvm.compiler.nodes.java.LoadIndexedNode; 41 import org.graalvm.compiler.nodes.java.NewArrayNode; 42 import org.graalvm.compiler.nodes.java.NewInstanceNode; 43 import org.graalvm.compiler.nodes.java.StoreFieldNode; 44 import org.graalvm.compiler.nodes.virtual.CommitAllocationNode; 45 import org.graalvm.compiler.phases.common.CanonicalizerPhase; 46 import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase; 47 48 /** 49 * The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct 50 * values. 51 */ 52 public class PartialEscapeAnalysisTest extends EATestBase { 53 54 public static class TestObject { 55 56 public int x; 57 public int y; 58 59 public TestObject(int x, int y) { 60 this.x = x; 61 this.y = y; 62 } 63 } 64 65 public static class TestObject2 { 66 67 public Object x; 68 public Object y; 69 70 public TestObject2(Object x, Object y) { 71 this.x = x; 72 this.y = y; 73 } 74 } 75 76 @Test 77 public void test1() { 78 testPartialEscapeAnalysis("test1Snippet", 0.25, 1); 79 } 80 81 @SuppressWarnings("all") 82 public static Object test1Snippet(int a, int b, Object x, Object y) { 83 TestObject2 obj = new TestObject2(x, y); 84 if (a < 0) { 85 if (b < 0) { 86 return obj; 87 } else { 88 return obj.y; 89 } 90 } else { 91 return obj.x; 92 } 93 } 94 95 @Test 96 public void test2() { 97 testPartialEscapeAnalysis("test2Snippet", 1.5, 3, LoadIndexedNode.class); 98 } 99 100 public static Object test2Snippet(int a, Object x, Object y, Object z) { 101 TestObject2 obj = new TestObject2(x, y); 102 obj.x = new TestObject2(obj, z); 103 if (a < 0) { 104 ((TestObject2) obj.x).y = null; 105 obj.y = null; 106 return obj; 107 } else { 108 ((TestObject2) obj.x).y = Integer.class; 109 ((TestObject2) obj.x).x = null; 110 return obj.x; 111 } 112 } 113 114 @Test 115 public void test3() { 116 testPartialEscapeAnalysis("test3Snippet", 0.5, 1, StoreFieldNode.class, LoadFieldNode.class); 117 } 118 119 public static Object test3Snippet(int a) { 120 if (a < 0) { 121 TestObject obj = new TestObject(1, 2); 122 obj.x = 123; 123 obj.y = 234; 124 obj.x = 123111; 125 obj.y = new Integer(123).intValue(); 126 return obj; 127 } else { 128 return null; 129 } 130 } 131 132 @Test 133 public void testArrayCopy() { 134 testPartialEscapeAnalysis("testArrayCopySnippet", 0, 0); 135 } 136 137 public static Object[] array = new Object[]{1, 2, 3, 4, 5, "asdf", "asdf"}; 138 139 public static Object testArrayCopySnippet(int a) { 140 Object[] tmp = new Object[]{a != 1 ? array[a] : null}; 141 Object[] tmp2 = new Object[5]; 142 System.arraycopy(tmp, 0, tmp2, 4, 1); 143 return tmp2[4]; 144 } 145 146 @Test 147 @Ignore 148 public void testCache() { 149 testPartialEscapeAnalysis("testCacheSnippet", 0.75, 1); 150 } 151 152 public static class CacheKey { 153 154 private final int idx; 155 private final Object ref; 156 157 public CacheKey(int idx, Object ref) { 158 this.idx = idx; 159 this.ref = ref; 160 } 161 162 @Override 163 public int hashCode() { 164 return 31 * idx + ref.hashCode(); 165 } 166 167 public synchronized boolean equals(CacheKey other) { 168 return idx == other.idx && ref == other.ref; 169 } 170 } 171 172 public static CacheKey cacheKey = null; 173 public static Object value = null; 174 175 private static native Object createValue(CacheKey key); 176 177 public static Object testCacheSnippet(int idx, Object ref) { 178 CacheKey key = new CacheKey(idx, ref); 179 if (!key.equals(cacheKey)) { 180 cacheKey = key; 181 value = createValue(key); 182 } 183 return value; 184 } 185 186 public static int testReference1Snippet(Object a) { 187 SoftReference<Object> softReference = new SoftReference<>(a); 188 if (softReference.get().hashCode() == 0) { 189 return 1; 190 } else { 191 return 2; 192 } 193 } 194 195 @Test 196 public void testReference1() { 197 prepareGraph("testReference1Snippet", false); 198 assertDeepEquals(1, graph.getNodes().filter(NewInstanceNode.class).count()); 199 } 200 201 public static int testCanonicalizeSnippet(int v) { 202 CacheKey key = new CacheKey(v, null); 203 204 CacheKey key2; 205 if (key.idx == v) { 206 key2 = new CacheKey(v, null); 207 } else { 208 key2 = null; 209 } 210 return key2.idx; 211 } 212 213 @Test 214 public void testCanonicalize() { 215 prepareGraph("testCanonicalizeSnippet", false); 216 assertTrue(graph.getNodes().filter(ReturnNode.class).count() == 1); 217 assertTrue(graph.getNodes().filter(ReturnNode.class).first().result() == graph.getParameter(0)); 218 } 219 220 public static int testBoxLoopSnippet(int n) { 221 Integer sum = 0; 222 for (Integer i = 0; i < n; i++) { 223 if (sum == null) { 224 sum = null; 225 } else { 226 sum += i; 227 } 228 } 229 return sum; 230 } 231 232 @Test 233 public void testBoxLoop() { 234 testPartialEscapeAnalysis("testBoxLoopSnippet", 0, 0, BoxNode.class, UnboxNode.class); 235 } 236 237 static volatile int staticField; 238 static boolean executedDeoptimizeDirective; 239 240 static class A { 241 String field; 242 } 243 244 public static Object deoptWithVirtualObjectsSnippet() { 245 A a = new A(); 246 a.field = "field"; 247 248 staticField = 5; 249 if (staticField == 5) { 250 GraalDirectives.deoptimize(); 251 executedDeoptimizeDirective = true; 252 } 253 254 return a.field; 255 } 256 257 /** 258 * Tests deoptimizing with virtual objects in debug info. 259 */ 260 @Test 261 public void testDeoptWithVirtualObjects() { 262 assertFalse(executedDeoptimizeDirective); 263 test("deoptWithVirtualObjectsSnippet"); 264 assertTrue(executedDeoptimizeDirective); 265 } 266 267 @SafeVarargs 268 protected final void testPartialEscapeAnalysis(String snippet, double expectedProbability, int expectedCount, Class<? extends Node>... invalidNodeClasses) { 269 prepareGraph(snippet, false); 270 for (AbstractMergeNode merge : graph.getNodes(AbstractMergeNode.TYPE)) { 271 merge.setStateAfter(null); 272 } 273 new DeadCodeEliminationPhase().apply(graph); 274 new CanonicalizerPhase().apply(graph, context); 275 try { 276 Assert.assertTrue("partial escape analysis should have removed all NewInstanceNode allocations", graph.getNodes().filter(NewInstanceNode.class).isEmpty()); 277 Assert.assertTrue("partial escape analysis should have removed all NewArrayNode allocations", graph.getNodes().filter(NewArrayNode.class).isEmpty()); 278 279 ControlFlowGraph cfg = ControlFlowGraph.compute(graph, true, true, false, false); 280 double probabilitySum = 0; 281 int materializeCount = 0; 282 for (CommitAllocationNode materialize : graph.getNodes().filter(CommitAllocationNode.class)) { 283 probabilitySum += cfg.blockFor(materialize).probability() * materialize.getVirtualObjects().size(); 284 materializeCount += materialize.getVirtualObjects().size(); 285 } 286 Assert.assertEquals("unexpected number of MaterializeObjectNodes", expectedCount, materializeCount); 287 Assert.assertEquals("unexpected probability of MaterializeObjectNodes", expectedProbability, probabilitySum, 0.01); 288 for (Node node : graph.getNodes()) { 289 for (Class<? extends Node> clazz : invalidNodeClasses) { 290 Assert.assertFalse("instance of invalid class: " + clazz.getSimpleName(), clazz.isInstance(node) && node.usages().isNotEmpty()); 291 } 292 } 293 } catch (AssertionError e) { 294 TypeSystemTest.outputGraph(graph, snippet + ": " + e.getMessage()); 295 throw e; 296 } 297 } 298 }