43972
|
1 |
/*
|
58299
|
2 |
* Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
|
43972
|
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 |
*/
|
50858
|
23 |
|
|
24 |
|
43972
|
25 |
package org.graalvm.compiler.core.test.ea;
|
|
26 |
|
46344
|
27 |
import java.util.List;
|
43972
|
28 |
|
|
29 |
import org.graalvm.compiler.graph.Node;
|
51736
|
30 |
import org.graalvm.compiler.graph.iterators.NodeIterable;
|
43972
|
31 |
import org.graalvm.compiler.loop.DefaultLoopPolicies;
|
|
32 |
import org.graalvm.compiler.loop.phases.LoopFullUnrollPhase;
|
|
33 |
import org.graalvm.compiler.loop.phases.LoopPeelingPhase;
|
46344
|
34 |
import org.graalvm.compiler.nodes.ConstantNode;
|
|
35 |
import org.graalvm.compiler.nodes.ReturnNode;
|
|
36 |
import org.graalvm.compiler.nodes.extended.BoxNode;
|
43972
|
37 |
import org.graalvm.compiler.nodes.extended.ValueAnchorNode;
|
46344
|
38 |
import org.graalvm.compiler.nodes.java.LoadFieldNode;
|
43972
|
39 |
import org.graalvm.compiler.nodes.virtual.AllocatedObjectNode;
|
|
40 |
import org.graalvm.compiler.nodes.virtual.CommitAllocationNode;
|
|
41 |
import org.graalvm.compiler.phases.schedule.SchedulePhase;
|
58299
|
42 |
import org.graalvm.compiler.test.SubprocessUtil;
|
43972
|
43 |
import org.graalvm.compiler.virtual.phases.ea.PartialEscapePhase;
|
46344
|
44 |
import org.junit.Assert;
|
49873
|
45 |
import org.junit.Assume;
|
46344
|
46 |
import org.junit.Test;
|
|
47 |
|
|
48 |
import jdk.vm.ci.meta.JavaConstant;
|
43972
|
49 |
|
|
50 |
/**
|
|
51 |
* The PartialEscapeAnalysisPhase is expected to remove all allocations and return the correct
|
|
52 |
* values.
|
|
53 |
*/
|
|
54 |
public class EscapeAnalysisTest extends EATestBase {
|
|
55 |
|
|
56 |
@Test
|
|
57 |
public void test1() {
|
|
58 |
testEscapeAnalysis("test1Snippet", JavaConstant.forInt(101), false);
|
|
59 |
}
|
|
60 |
|
51436
|
61 |
@SuppressWarnings("deprecation")
|
43972
|
62 |
public static int test1Snippet() {
|
|
63 |
Integer x = new Integer(101);
|
|
64 |
return x.intValue();
|
|
65 |
}
|
|
66 |
|
|
67 |
@Test
|
|
68 |
public void test2() {
|
|
69 |
testEscapeAnalysis("test2Snippet", JavaConstant.forInt(0), false);
|
|
70 |
}
|
|
71 |
|
|
72 |
public static int test2Snippet() {
|
|
73 |
Integer[] x = new Integer[0];
|
|
74 |
return x.length;
|
|
75 |
}
|
|
76 |
|
|
77 |
@Test
|
|
78 |
public void test3() {
|
|
79 |
testEscapeAnalysis("test3Snippet", JavaConstant.NULL_POINTER, false);
|
|
80 |
}
|
|
81 |
|
|
82 |
public static Object test3Snippet() {
|
|
83 |
Integer[] x = new Integer[1];
|
|
84 |
return x[0];
|
|
85 |
}
|
|
86 |
|
|
87 |
@Test
|
|
88 |
public void testMonitor() {
|
|
89 |
testEscapeAnalysis("testMonitorSnippet", JavaConstant.forInt(0), false);
|
|
90 |
}
|
|
91 |
|
51436
|
92 |
@SuppressWarnings("deprecation")
|
43972
|
93 |
public static int testMonitorSnippet() {
|
|
94 |
Integer x = new Integer(0);
|
|
95 |
Double y = new Double(0);
|
|
96 |
Object z = new Object();
|
|
97 |
synchronized (x) {
|
|
98 |
synchronized (y) {
|
|
99 |
synchronized (z) {
|
|
100 |
notInlineable();
|
|
101 |
}
|
|
102 |
}
|
|
103 |
}
|
|
104 |
return x.intValue();
|
|
105 |
}
|
|
106 |
|
|
107 |
@Test
|
|
108 |
public void testMonitor2() {
|
|
109 |
testEscapeAnalysis("testMonitor2Snippet", JavaConstant.forInt(0), false);
|
|
110 |
}
|
|
111 |
|
|
112 |
/**
|
|
113 |
* This test case differs from the last one in that it requires inlining within a synchronized
|
|
114 |
* region.
|
|
115 |
*/
|
51436
|
116 |
@SuppressWarnings("deprecation")
|
43972
|
117 |
public static int testMonitor2Snippet() {
|
|
118 |
Integer x = new Integer(0);
|
|
119 |
Double y = new Double(0);
|
|
120 |
Object z = new Object();
|
|
121 |
synchronized (x) {
|
|
122 |
synchronized (y) {
|
|
123 |
synchronized (z) {
|
|
124 |
notInlineable();
|
|
125 |
return x.intValue();
|
|
126 |
}
|
|
127 |
}
|
|
128 |
}
|
|
129 |
}
|
|
130 |
|
|
131 |
@Test
|
|
132 |
public void testMerge() {
|
|
133 |
testEscapeAnalysis("testMerge1Snippet", JavaConstant.forInt(0), true);
|
|
134 |
}
|
|
135 |
|
|
136 |
public static int testMerge1Snippet(int a) {
|
|
137 |
TestClassInt obj = new TestClassInt(1, 0);
|
|
138 |
if (a < 0) {
|
|
139 |
obj.x = obj.x + 1;
|
|
140 |
} else {
|
|
141 |
obj.x = obj.x + 2;
|
|
142 |
obj.y = 0;
|
|
143 |
}
|
|
144 |
if (obj.x > 1000) {
|
|
145 |
return 1;
|
|
146 |
}
|
|
147 |
return obj.y;
|
|
148 |
}
|
|
149 |
|
|
150 |
@Test
|
|
151 |
public void testSimpleLoop() {
|
|
152 |
testEscapeAnalysis("testSimpleLoopSnippet", JavaConstant.forInt(1), false);
|
|
153 |
}
|
|
154 |
|
|
155 |
public int testSimpleLoopSnippet(int a) {
|
|
156 |
TestClassInt obj = new TestClassInt(1, 2);
|
|
157 |
for (int i = 0; i < a; i++) {
|
|
158 |
notInlineable();
|
|
159 |
}
|
|
160 |
return obj.x;
|
|
161 |
}
|
|
162 |
|
|
163 |
@Test
|
|
164 |
public void testModifyingLoop() {
|
|
165 |
testEscapeAnalysis("testModifyingLoopSnippet", JavaConstant.forInt(1), false);
|
|
166 |
}
|
|
167 |
|
|
168 |
public int testModifyingLoopSnippet(int a) {
|
|
169 |
TestClassInt obj = new TestClassInt(1, 2);
|
|
170 |
for (int i = 0; i < a; i++) {
|
|
171 |
obj.x = 3;
|
|
172 |
notInlineable();
|
|
173 |
}
|
|
174 |
return obj.x <= 3 ? 1 : 0;
|
|
175 |
}
|
|
176 |
|
|
177 |
@Test
|
|
178 |
public void testMergeAllocationsInt() {
|
|
179 |
testEscapeAnalysis("testMergeAllocationsIntSnippet", JavaConstant.forInt(1), false);
|
|
180 |
}
|
|
181 |
|
|
182 |
public int testMergeAllocationsIntSnippet(int a) {
|
|
183 |
TestClassInt obj;
|
|
184 |
if (a < 0) {
|
|
185 |
obj = new TestClassInt(1, 2);
|
|
186 |
notInlineable();
|
|
187 |
} else {
|
|
188 |
obj = new TestClassInt(1, 2);
|
|
189 |
notInlineable();
|
|
190 |
}
|
|
191 |
return obj.x <= 3 ? 1 : 0;
|
|
192 |
}
|
|
193 |
|
|
194 |
@Test
|
46344
|
195 |
public void testMergeAllocationsInt2() {
|
|
196 |
testEscapeAnalysis("testMergeAllocationsInt2Snippet", JavaConstant.forInt(1), true);
|
|
197 |
}
|
|
198 |
|
|
199 |
public int testMergeAllocationsInt2Snippet(int a) {
|
|
200 |
/*
|
|
201 |
* The initial object in obj exists until the end of the function, but it can still be
|
|
202 |
* merged with the one allocated in the else block because noone can observe the identity.
|
|
203 |
*/
|
|
204 |
TestClassInt obj = new TestClassInt(1, 2);
|
|
205 |
if (a < 0) {
|
|
206 |
notInlineable();
|
|
207 |
} else {
|
|
208 |
obj = new TestClassInt(1, 2);
|
|
209 |
notInlineable();
|
|
210 |
}
|
|
211 |
return obj.x <= 3 ? 1 : 0;
|
|
212 |
}
|
|
213 |
|
|
214 |
@Test
|
|
215 |
public void testMergeAllocationsInt3() {
|
|
216 |
// ensure that the result is not constant:
|
|
217 |
assertTrue(testMergeAllocationsInt3Snippet(true));
|
|
218 |
assertFalse(testMergeAllocationsInt3Snippet(false));
|
|
219 |
|
|
220 |
prepareGraph("testMergeAllocationsInt3Snippet", true);
|
|
221 |
assertFalse(graph.getNodes().filter(ReturnNode.class).first().result().isConstant());
|
|
222 |
}
|
|
223 |
|
|
224 |
public boolean testMergeAllocationsInt3Snippet(boolean a) {
|
|
225 |
TestClassInt phi1;
|
|
226 |
TestClassInt phi2;
|
|
227 |
if (a) {
|
|
228 |
field = new TestClassObject();
|
|
229 |
field = new TestClassObject();
|
|
230 |
phi1 = phi2 = new TestClassInt(1, 2);
|
|
231 |
} else {
|
|
232 |
phi1 = new TestClassInt(2, 3);
|
|
233 |
phi2 = new TestClassInt(3, 4);
|
|
234 |
}
|
|
235 |
return phi1 == phi2;
|
|
236 |
}
|
|
237 |
|
|
238 |
@Test
|
43972
|
239 |
public void testMergeAllocationsObj() {
|
|
240 |
testEscapeAnalysis("testMergeAllocationsObjSnippet", JavaConstant.forInt(1), false);
|
|
241 |
}
|
|
242 |
|
|
243 |
public int testMergeAllocationsObjSnippet(int a) {
|
|
244 |
TestClassObject obj;
|
|
245 |
Integer one = 1;
|
|
246 |
Integer two = 2;
|
|
247 |
Integer three = 3;
|
|
248 |
if (a < 0) {
|
|
249 |
obj = new TestClassObject(one, two);
|
|
250 |
notInlineable();
|
|
251 |
} else {
|
|
252 |
obj = new TestClassObject(one, three);
|
|
253 |
notInlineable();
|
|
254 |
}
|
|
255 |
return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
|
|
256 |
}
|
|
257 |
|
|
258 |
@Test
|
|
259 |
public void testMergeAllocationsObjCirc() {
|
|
260 |
testEscapeAnalysis("testMergeAllocationsObjCircSnippet", JavaConstant.forInt(1), false);
|
|
261 |
}
|
|
262 |
|
|
263 |
public int testMergeAllocationsObjCircSnippet(int a) {
|
|
264 |
TestClassObject obj;
|
|
265 |
Integer one = 1;
|
|
266 |
Integer two = 2;
|
|
267 |
Integer three = 3;
|
|
268 |
if (a < 0) {
|
|
269 |
obj = new TestClassObject(one);
|
|
270 |
obj.y = obj;
|
|
271 |
obj.y = two;
|
|
272 |
notInlineable();
|
|
273 |
} else {
|
|
274 |
obj = new TestClassObject(one);
|
|
275 |
obj.y = obj;
|
|
276 |
obj.y = three;
|
|
277 |
notInlineable();
|
|
278 |
}
|
|
279 |
return ((Integer) obj.x).intValue() <= 3 ? 1 : 0;
|
|
280 |
}
|
|
281 |
|
|
282 |
static class MyException extends RuntimeException {
|
|
283 |
|
|
284 |
private static final long serialVersionUID = 0L;
|
|
285 |
|
|
286 |
protected Integer value;
|
|
287 |
|
|
288 |
MyException(Integer value) {
|
|
289 |
super((Throwable) null);
|
|
290 |
this.value = value;
|
|
291 |
}
|
|
292 |
|
|
293 |
@SuppressWarnings("sync-override")
|
|
294 |
@Override
|
|
295 |
public final Throwable fillInStackTrace() {
|
54084
|
296 |
return this;
|
43972
|
297 |
}
|
|
298 |
}
|
|
299 |
|
|
300 |
@Test
|
|
301 |
public void testMergeAllocationsException() {
|
|
302 |
testEscapeAnalysis("testMergeAllocationsExceptionSnippet", JavaConstant.forInt(1), false);
|
|
303 |
}
|
|
304 |
|
|
305 |
public int testMergeAllocationsExceptionSnippet(int a) {
|
|
306 |
MyException obj;
|
|
307 |
Integer one = 1;
|
|
308 |
if (a < 0) {
|
|
309 |
obj = new MyException(one);
|
|
310 |
notInlineable();
|
|
311 |
} else {
|
|
312 |
obj = new MyException(one);
|
|
313 |
notInlineable();
|
|
314 |
}
|
|
315 |
return obj.value <= 3 ? 1 : 0;
|
|
316 |
}
|
|
317 |
|
46344
|
318 |
/**
|
|
319 |
* Tests that a graph with allocations that does not make progress during PEA will not be
|
|
320 |
* changed.
|
|
321 |
*/
|
|
322 |
@Test
|
|
323 |
public void testChangeHandling() {
|
|
324 |
prepareGraph("testChangeHandlingSnippet", false);
|
|
325 |
Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count());
|
|
326 |
Assert.assertEquals(1, graph.getNodes().filter(BoxNode.class).count());
|
|
327 |
List<Node> nodes = graph.getNodes().snapshot();
|
|
328 |
// verify that an additional run doesn't add or remove nodes
|
58877
|
329 |
new PartialEscapePhase(false, false, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
|
46344
|
330 |
Assert.assertEquals(nodes.size(), graph.getNodeCount());
|
|
331 |
for (Node node : nodes) {
|
|
332 |
Assert.assertTrue(node.isAlive());
|
|
333 |
}
|
|
334 |
}
|
|
335 |
|
|
336 |
public volatile Object field;
|
|
337 |
|
51436
|
338 |
@SuppressWarnings("deprecation")
|
46344
|
339 |
public int testChangeHandlingSnippet(int a) {
|
|
340 |
Object obj;
|
|
341 |
Integer one = 1;
|
|
342 |
obj = new MyException(one);
|
|
343 |
if (a < 0) {
|
|
344 |
notInlineable();
|
|
345 |
} else {
|
|
346 |
obj = new Integer(1);
|
|
347 |
notInlineable();
|
|
348 |
}
|
|
349 |
field = obj;
|
|
350 |
return 1;
|
|
351 |
}
|
|
352 |
|
|
353 |
/**
|
|
354 |
* Test the case where allocations before and during a loop that have no usages other than their
|
|
355 |
* phi need to be recognized as an important change. This needs a loop so that the allocation is
|
|
356 |
* not trivially removed by dead code elimination.
|
|
357 |
*/
|
|
358 |
@Test
|
|
359 |
public void testRemovalSpecialCase() {
|
|
360 |
prepareGraph("testRemovalSpecialCaseSnippet", false);
|
|
361 |
Assert.assertEquals(2, graph.getNodes().filter(CommitAllocationNode.class).count());
|
|
362 |
// create the situation by removing the if
|
|
363 |
graph.replaceFixedWithFloating(graph.getNodes().filter(LoadFieldNode.class).first(), graph.unique(ConstantNode.forInt(0)));
|
58877
|
364 |
createCanonicalizerPhase().apply(graph, context);
|
46344
|
365 |
// verify that an additional run removes all allocations
|
58877
|
366 |
new PartialEscapePhase(false, false, createCanonicalizerPhase(), null, graph.getOptions()).apply(graph, context);
|
46344
|
367 |
Assert.assertEquals(0, graph.getNodes().filter(CommitAllocationNode.class).count());
|
|
368 |
}
|
|
369 |
|
|
370 |
public volatile int field2;
|
|
371 |
|
|
372 |
public int testRemovalSpecialCaseSnippet(int a) {
|
|
373 |
Object phi = new Object();
|
|
374 |
for (int i = 0; i < a; i++) {
|
|
375 |
field = null;
|
|
376 |
if (field2 == 1) {
|
|
377 |
phi = new Object();
|
|
378 |
}
|
|
379 |
}
|
|
380 |
return phi == null ? 1 : 0;
|
|
381 |
}
|
|
382 |
|
43972
|
383 |
@Test
|
|
384 |
public void testCheckCast() {
|
46344
|
385 |
testEscapeAnalysis("testCheckCastSnippet", getSnippetReflection().forObject(TestClassObject.class), true);
|
43972
|
386 |
}
|
|
387 |
|
|
388 |
public Object testCheckCastSnippet() {
|
|
389 |
TestClassObject obj = new TestClassObject(TestClassObject.class);
|
|
390 |
TestClassObject obj2 = new TestClassObject(obj);
|
|
391 |
return ((TestClassObject) obj2.x).x;
|
|
392 |
}
|
|
393 |
|
|
394 |
@Test
|
|
395 |
public void testInstanceOf() {
|
|
396 |
testEscapeAnalysis("testInstanceOfSnippet", JavaConstant.forInt(1), false);
|
|
397 |
}
|
|
398 |
|
|
399 |
public boolean testInstanceOfSnippet() {
|
|
400 |
TestClassObject obj = new TestClassObject(TestClassObject.class);
|
|
401 |
TestClassObject obj2 = new TestClassObject(obj);
|
|
402 |
return obj2.x instanceof TestClassObject;
|
|
403 |
}
|
|
404 |
|
|
405 |
@SuppressWarnings("unused")
|
|
406 |
public static void testNewNodeSnippet() {
|
|
407 |
new ValueAnchorNode(null);
|
|
408 |
}
|
|
409 |
|
|
410 |
/**
|
|
411 |
* This test makes sure that the allocation of a {@link Node} can be removed. It therefore also
|
|
412 |
* tests the intrinsification of {@link Object#getClass()}.
|
|
413 |
*/
|
|
414 |
@Test
|
|
415 |
public void testNewNode() {
|
58299
|
416 |
// Tracking of creation interferes with escape analysis
|
49873
|
417 |
Assume.assumeFalse(Node.TRACK_CREATION_POSITION);
|
58299
|
418 |
// JaCoco can add escaping allocations (e.g. allocation of coverage recording data
|
|
419 |
// structures)
|
|
420 |
Assume.assumeFalse("JaCoCo found -> skipping", SubprocessUtil.isJaCoCoAttached());
|
43972
|
421 |
testEscapeAnalysis("testNewNodeSnippet", null, false);
|
|
422 |
}
|
|
423 |
|
|
424 |
private static final TestClassObject staticObj = new TestClassObject();
|
|
425 |
|
|
426 |
public static Object testFullyUnrolledLoopSnippet() {
|
|
427 |
/*
|
|
428 |
* This tests a case that can appear if PEA is performed both before and after loop
|
|
429 |
* unrolling/peeling: If the VirtualInstanceNode is not duplicated correctly with the loop,
|
|
430 |
* the resulting object will reference itself, and not a second (different) object.
|
|
431 |
*/
|
|
432 |
TestClassObject obj = staticObj;
|
|
433 |
for (int i = 0; i < 2; i++) {
|
|
434 |
obj = new TestClassObject(obj);
|
|
435 |
}
|
|
436 |
return obj.x;
|
|
437 |
}
|
|
438 |
|
|
439 |
@Test
|
|
440 |
public void testFullyUnrolledLoop() {
|
|
441 |
prepareGraph("testFullyUnrolledLoopSnippet", false);
|
58877
|
442 |
new LoopFullUnrollPhase(createCanonicalizerPhase(), new DefaultLoopPolicies()).apply(graph, context);
|
|
443 |
new PartialEscapePhase(false, createCanonicalizerPhase(), graph.getOptions()).apply(graph, context);
|
43972
|
444 |
Assert.assertEquals(1, returnNodes.size());
|
|
445 |
Assert.assertTrue(returnNodes.get(0).result() instanceof AllocatedObjectNode);
|
|
446 |
CommitAllocationNode commit = ((AllocatedObjectNode) returnNodes.get(0).result()).getCommit();
|
|
447 |
Assert.assertEquals(2, commit.getValues().size());
|
|
448 |
Assert.assertEquals(1, commit.getVirtualObjects().size());
|
|
449 |
Assert.assertTrue("non-cyclic data structure expected", commit.getVirtualObjects().get(0) != commit.getValues().get(0));
|
|
450 |
}
|
|
451 |
|
|
452 |
@SuppressWarnings("unused") private static Object staticField;
|
|
453 |
|
|
454 |
private static TestClassObject inlinedPart(TestClassObject obj) {
|
|
455 |
TestClassObject ret = new TestClassObject(obj);
|
|
456 |
staticField = null;
|
|
457 |
return ret;
|
|
458 |
}
|
|
459 |
|
|
460 |
public static Object testPeeledLoopSnippet() {
|
|
461 |
TestClassObject obj = staticObj;
|
|
462 |
int i = 0;
|
|
463 |
do {
|
|
464 |
obj = inlinedPart(obj);
|
|
465 |
} while (i++ < 10);
|
|
466 |
staticField = obj;
|
|
467 |
return obj.x;
|
|
468 |
}
|
|
469 |
|
|
470 |
@Test
|
|
471 |
public void testPeeledLoop() {
|
|
472 |
prepareGraph("testPeeledLoopSnippet", false);
|
|
473 |
new LoopPeelingPhase(new DefaultLoopPolicies()).apply(graph, getDefaultHighTierContext());
|
46344
|
474 |
new SchedulePhase(graph.getOptions()).apply(graph);
|
43972
|
475 |
}
|
|
476 |
|
|
477 |
public static void testDeoptMonitorSnippetInner(Object o2, Object t, int i) {
|
|
478 |
staticField = null;
|
|
479 |
if (i == 0) {
|
|
480 |
staticField = o2;
|
|
481 |
Number n = (Number) t;
|
|
482 |
n.toString();
|
|
483 |
}
|
|
484 |
}
|
|
485 |
|
|
486 |
public static void testDeoptMonitorSnippet(Object t, int i) {
|
|
487 |
TestClassObject o = new TestClassObject();
|
|
488 |
TestClassObject o2 = new TestClassObject(o);
|
|
489 |
|
|
490 |
synchronized (o) {
|
|
491 |
testDeoptMonitorSnippetInner(o2, t, i);
|
|
492 |
}
|
|
493 |
}
|
|
494 |
|
|
495 |
@Test
|
|
496 |
public void testDeoptMonitor() {
|
|
497 |
test("testDeoptMonitorSnippet", new Object(), 0);
|
|
498 |
}
|
51736
|
499 |
|
|
500 |
@Test
|
|
501 |
public void testInterfaceArrayAssignment() {
|
|
502 |
prepareGraph("testInterfaceArrayAssignmentSnippet", false);
|
|
503 |
NodeIterable<ReturnNode> returns = graph.getNodes().filter(ReturnNode.class);
|
|
504 |
assertTrue(returns.count() == 1);
|
|
505 |
assertFalse(returns.first().result().isConstant());
|
|
506 |
}
|
|
507 |
|
|
508 |
private interface TestInterface {
|
|
509 |
}
|
|
510 |
|
|
511 |
public static boolean testInterfaceArrayAssignmentSnippet() {
|
|
512 |
Object[] array = new TestInterface[1];
|
|
513 |
array[0] = new Object();
|
|
514 |
return array[0] == null;
|
|
515 |
}
|
52910
|
516 |
|
|
517 |
static final class Complex {
|
|
518 |
private final double real;
|
|
519 |
private final double imag;
|
|
520 |
|
|
521 |
Complex(double real, double imag) {
|
|
522 |
this.real = real;
|
|
523 |
this.imag = imag;
|
|
524 |
}
|
|
525 |
|
|
526 |
public Complex mul(Complex other) {
|
|
527 |
return new Complex(real * other.real - imag * other.imag, imag * other.real + real * other.imag);
|
|
528 |
}
|
|
529 |
|
|
530 |
public Complex add(Complex other) {
|
|
531 |
return new Complex(real + other.real, imag + other.imag);
|
|
532 |
}
|
|
533 |
|
|
534 |
// equals is needed for result comparison
|
|
535 |
|
|
536 |
@Override
|
|
537 |
public boolean equals(Object obj) {
|
|
538 |
if (obj == null || getClass() != obj.getClass()) {
|
|
539 |
return false;
|
|
540 |
}
|
|
541 |
Complex other = (Complex) obj;
|
|
542 |
return this == other || Double.doubleToLongBits(imag) == Double.doubleToLongBits(other.imag) && Double.doubleToLongBits(real) == Double.doubleToLongBits(other.real);
|
|
543 |
}
|
|
544 |
|
|
545 |
@Override
|
|
546 |
public int hashCode() {
|
|
547 |
return Double.hashCode(real) ^ Double.hashCode(imag);
|
|
548 |
}
|
|
549 |
}
|
|
550 |
|
|
551 |
private static final Complex[][] inputValue = new Complex[100][100];
|
|
552 |
static {
|
|
553 |
for (int i = 0; i < 100; i++) {
|
|
554 |
for (int j = 0; j < 100; j++) {
|
|
555 |
inputValue[i][j] = new Complex(i, j);
|
|
556 |
}
|
|
557 |
}
|
|
558 |
}
|
|
559 |
|
|
560 |
public static Complex[][] testComplexMultiplySnippet1(Complex[][] input) {
|
|
561 |
int size = input.length;
|
|
562 |
Complex[][] result = new Complex[size][size];
|
|
563 |
for (int i = 0; i < size; i++) {
|
|
564 |
for (int j = 0; j < size; j++) {
|
|
565 |
Complex s = new Complex(0, 0);
|
|
566 |
for (int k = 0; k < size; k++) {
|
|
567 |
s = s.add(input[i][k].mul(input[k][j]));
|
|
568 |
}
|
|
569 |
result[i][j] = s;
|
|
570 |
}
|
|
571 |
}
|
|
572 |
return result;
|
|
573 |
}
|
|
574 |
|
|
575 |
@Test
|
|
576 |
public void testComplexMultiply1() {
|
|
577 |
test("testComplexMultiplySnippet1", (Object) inputValue);
|
|
578 |
|
|
579 |
// EA test: only one allocation remains (not counting the NewMultiArray), using iterative EA
|
|
580 |
testEscapeAnalysis("testComplexMultiplySnippet1", null, true, 1);
|
|
581 |
}
|
|
582 |
|
|
583 |
public static Complex[][] testComplexMultiplySnippet2(Complex[][] input) {
|
|
584 |
int size = input.length;
|
|
585 |
Complex[][] result = new Complex[size][size];
|
|
586 |
for (int i = 0; i < size; i++) {
|
|
587 |
for (int j = 0; j < size; j++) {
|
|
588 |
Complex s = input[i][0].mul(input[0][j]);
|
|
589 |
for (int k = 1; k < size; k++) {
|
|
590 |
s = s.add(input[i][k].mul(input[k][j]));
|
|
591 |
}
|
|
592 |
result[i][j] = s;
|
|
593 |
}
|
|
594 |
}
|
|
595 |
return result;
|
|
596 |
}
|
|
597 |
|
|
598 |
@Test
|
|
599 |
public void testComplexMultiply2() {
|
|
600 |
test("testComplexMultiplySnippet2", (Object) inputValue);
|
|
601 |
|
|
602 |
// EA test: only one allocation remains (not counting the NewMultiArray), using iterative EA
|
|
603 |
testEscapeAnalysis("testComplexMultiplySnippet2", null, true, 1);
|
|
604 |
}
|
|
605 |
|
|
606 |
public static Complex testComplexAddSnippet(Complex[][] input) {
|
|
607 |
int size = input.length;
|
|
608 |
Complex s = new Complex(0, 0);
|
|
609 |
for (int i = 0; i < size; i++) {
|
|
610 |
Complex s2 = new Complex(0, 0);
|
|
611 |
for (int j = 0; j < size; j++) {
|
|
612 |
s2 = s2.add(input[i][j]);
|
|
613 |
}
|
|
614 |
s.add(s2);
|
|
615 |
}
|
|
616 |
return s;
|
|
617 |
}
|
|
618 |
|
|
619 |
@Test
|
|
620 |
public void testComplexAdd() {
|
|
621 |
test("testComplexAddSnippet", (Object) inputValue);
|
|
622 |
|
|
623 |
// EA test: only one allocation remains (not counting the NewMultiArray), using iterative EA
|
|
624 |
testEscapeAnalysis("testComplexAddSnippet", null, true, 1);
|
|
625 |
}
|
|
626 |
|
|
627 |
public static Complex[] testComplexRowSumSnippet(Complex[][] input) {
|
|
628 |
int size = input.length;
|
|
629 |
Complex[] result = new Complex[size];
|
|
630 |
for (int i = 0; i < size; i++) {
|
|
631 |
Complex s = new Complex(0, 0);
|
|
632 |
for (int j = 0; j < size; j++) {
|
|
633 |
s = s.add(input[i][j]);
|
|
634 |
}
|
|
635 |
result[i] = s;
|
|
636 |
}
|
|
637 |
return result;
|
|
638 |
}
|
|
639 |
|
|
640 |
@Test
|
|
641 |
public void testComplexRowSum() {
|
|
642 |
test("testComplexRowSumSnippet", (Object) inputValue);
|
|
643 |
|
|
644 |
// EA test: only two allocations (new array and new instance) remain
|
|
645 |
testEscapeAnalysis("testComplexRowSumSnippet", null, true, 2);
|
|
646 |
}
|
43972
|
647 |
}
|