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);
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
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")
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
|
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);
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
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")
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
|