4e77cf881d
Reviewed-by: kvn
1535 lines
49 KiB
Java
1535 lines
49 KiB
Java
/*
|
|
* Copyright (c) 2023, 2024, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*/
|
|
package compiler.c2.irTests.scalarReplacement;
|
|
|
|
import java.util.Random;
|
|
import jdk.test.lib.Asserts;
|
|
import compiler.lib.ir_framework.*;
|
|
|
|
/*
|
|
* @test
|
|
* @bug 8281429
|
|
* @summary Tests that C2 can correctly scalar replace some object allocation merges.
|
|
* @library /test/lib /
|
|
* @requires vm.debug == true & vm.flagless & vm.bits == 64 & vm.compiler2.enabled & vm.opt.final.EliminateAllocations
|
|
* @run driver compiler.c2.irTests.scalarReplacement.AllocationMergesTests
|
|
*/
|
|
public class AllocationMergesTests {
|
|
private int invocations = 0;
|
|
private static Point global_escape = new Point(2022, 2023);
|
|
|
|
public static void main(String[] args) {
|
|
TestFramework framework = new TestFramework();
|
|
|
|
Scenario scenario0 = new Scenario(0, "-XX:+UnlockDiagnosticVMOptions",
|
|
"-XX:+ReduceAllocationMerges",
|
|
"-XX:+TraceReduceAllocationMerges",
|
|
"-XX:+DeoptimizeALot",
|
|
"-XX:+UseCompressedOops",
|
|
"-XX:+UseCompressedClassPointers",
|
|
"-XX:CompileCommand=inline,*::charAt*",
|
|
"-XX:CompileCommand=inline,*PicturePositions::*",
|
|
"-XX:CompileCommand=inline,*Point::*",
|
|
"-XX:CompileCommand=inline,*Nested::*",
|
|
"-XX:CompileCommand=exclude,*::dummy*");
|
|
|
|
Scenario scenario1 = new Scenario(1, "-XX:+UnlockDiagnosticVMOptions",
|
|
"-XX:+ReduceAllocationMerges",
|
|
"-XX:+TraceReduceAllocationMerges",
|
|
"-XX:+DeoptimizeALot",
|
|
"-XX:+UseCompressedOops",
|
|
"-XX:-UseCompressedClassPointers",
|
|
"-XX:CompileCommand=inline,*::charAt*",
|
|
"-XX:CompileCommand=inline,*PicturePositions::*",
|
|
"-XX:CompileCommand=inline,*Point::*",
|
|
"-XX:CompileCommand=inline,*Nested::*",
|
|
"-XX:CompileCommand=exclude,*::dummy*");
|
|
|
|
Scenario scenario2 = new Scenario(2, "-XX:+UnlockDiagnosticVMOptions",
|
|
"-XX:+ReduceAllocationMerges",
|
|
"-XX:+TraceReduceAllocationMerges",
|
|
"-XX:+DeoptimizeALot",
|
|
"-XX:-UseCompressedOops",
|
|
"-XX:CompileCommand=inline,*::charAt*",
|
|
"-XX:CompileCommand=inline,*PicturePositions::*",
|
|
"-XX:CompileCommand=inline,*Point::*",
|
|
"-XX:CompileCommand=inline,*Nested::*",
|
|
"-XX:CompileCommand=exclude,*::dummy*");
|
|
|
|
framework.addScenarios(scenario0, scenario1, scenario2).start();
|
|
}
|
|
|
|
// ------------------ No Scalar Replacement Should Happen in The Tests Below ------------------- //
|
|
|
|
@Run(test = {"testGlobalEscape_C2",
|
|
"testArgEscape_C2",
|
|
"testEscapeInCallAfterMerge_C2",
|
|
"testNoEscapeWithWriteInLoop_C2",
|
|
"testPollutedWithWrite_C2",
|
|
"testPollutedPolymorphic_C2",
|
|
"testMergedLoadAfterDirectStore_C2",
|
|
"testMergedAccessAfterCallWithWrite_C2",
|
|
"testLoadAfterTrap_C2",
|
|
"testCondAfterMergeWithNull_C2",
|
|
"testLoadAfterLoopAlias_C2",
|
|
"testCallTwoSide_C2",
|
|
"testMergedAccessAfterCallNoWrite_C2",
|
|
"testCmpMergeWithNull_Second_C2",
|
|
"testObjectIdentity_C2",
|
|
"testSubclassesTrapping_C2",
|
|
"testCmpMergeWithNull_C2",
|
|
"testSubclasses_C2",
|
|
"testPartialPhis_C2",
|
|
"testPollutedNoWrite_C2",
|
|
"testThreeWayAliasedAlloc_C2",
|
|
"TestTrapAfterMerge_C2",
|
|
"testNestedObjectsObject_C2",
|
|
"testNestedObjectsNoEscapeObject_C2",
|
|
"testNestedObjectsArray_C2",
|
|
"testTrappingAfterMerge_C2",
|
|
"testSimpleAliasedAlloc_C2",
|
|
"testSimpleDoubleMerge_C2",
|
|
"testConsecutiveSimpleMerge_C2",
|
|
"testDoubleIfElseMerge_C2",
|
|
"testNoEscapeWithLoadInLoop_C2",
|
|
"testCmpAfterMerge_C2",
|
|
"testCondAfterMergeWithAllocate_C2",
|
|
"testCondLoadAfterMerge_C2",
|
|
"testIfElseInLoop_C2",
|
|
"testLoadInCondAfterMerge_C2",
|
|
"testLoadInLoop_C2",
|
|
"testMergesAndMixedEscape_C2",
|
|
"testSRAndNSR_NoTrap_C2",
|
|
"testSRAndNSR_Trap_C2",
|
|
"testString_one_C2",
|
|
"testString_two_C2",
|
|
"testLoadKlassFromCast_C2",
|
|
"testLoadKlassFromPhi_C2",
|
|
"testReReduce_C2"
|
|
})
|
|
public void runner(RunInfo info) {
|
|
invocations++;
|
|
|
|
Random random = info.getRandom();
|
|
boolean cond1 = invocations % 2 == 0;
|
|
boolean cond2 = !cond1;
|
|
|
|
int l = random.nextInt();
|
|
int w = random.nextInt();
|
|
int x = random.nextInt();
|
|
int y = random.nextInt();
|
|
int z = random.nextInt();
|
|
|
|
Asserts.assertEQ(testGlobalEscape_Interp(x, y), testGlobalEscape_C2(x, y));
|
|
Asserts.assertEQ(testArgEscape_Interp(x, y), testArgEscape_C2(x, y));
|
|
Asserts.assertEQ(testEscapeInCallAfterMerge_Interp(cond1, cond2, x, y), testEscapeInCallAfterMerge_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testNoEscapeWithWriteInLoop_Interp(cond1, cond2, x, y), testNoEscapeWithWriteInLoop_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testPollutedWithWrite_Interp(cond1, x), testPollutedWithWrite_C2(cond1, x));
|
|
Asserts.assertEQ(testPollutedPolymorphic_Interp(cond1, x), testPollutedPolymorphic_C2(cond1, x));
|
|
Asserts.assertEQ(testMergedLoadAfterDirectStore_Interp(cond1, x, y), testMergedLoadAfterDirectStore_C2(cond1, x, y));
|
|
Asserts.assertEQ(testMergedAccessAfterCallWithWrite_Interp(cond1, x, y), testMergedAccessAfterCallWithWrite_C2(cond1, x, y));
|
|
Asserts.assertEQ(testLoadAfterTrap_Interp(cond1, x, y), testLoadAfterTrap_C2(cond1, x, y));
|
|
Asserts.assertEQ(testCondAfterMergeWithNull_Interp(cond1, cond2, x, y), testCondAfterMergeWithNull_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testLoadAfterLoopAlias_Interp(x, y), testLoadAfterLoopAlias_C2(x, y));
|
|
Asserts.assertEQ(testCallTwoSide_Interp(cond1, x, y), testCallTwoSide_C2(cond1, x, y));
|
|
Asserts.assertEQ(testMergedAccessAfterCallNoWrite_Interp(cond1, x, y), testMergedAccessAfterCallNoWrite_C2(cond1, x, y));
|
|
Asserts.assertEQ(testCmpMergeWithNull_Second_Interp(cond1, x, y), testCmpMergeWithNull_Second_C2(cond1, x, y));
|
|
Asserts.assertEQ(testObjectIdentity_Interp(cond1, 42, y), testObjectIdentity_C2(cond1, 42, y));
|
|
Asserts.assertEQ(testSubclassesTrapping_Interp(cond1, cond2, x, y, w, z), testSubclassesTrapping_C2(cond1, cond2, x, y, w, z));
|
|
Asserts.assertEQ(testCmpMergeWithNull_Interp(cond1, x, y), testCmpMergeWithNull_C2(cond1, x, y));
|
|
Asserts.assertEQ(testSubclasses_Interp(cond1, cond2, x, y, w, z), testSubclasses_C2(cond1, cond2, x, y, w, z));
|
|
Asserts.assertEQ(testPartialPhis_Interp(cond1, l, x, y), testPartialPhis_C2(cond1, l, x, y));
|
|
Asserts.assertEQ(testPollutedNoWrite_Interp(cond1, l), testPollutedNoWrite_C2(cond1, l));
|
|
Asserts.assertEQ(testThreeWayAliasedAlloc_Interp(cond1, x, y), testThreeWayAliasedAlloc_C2(cond1, x, y));
|
|
Asserts.assertEQ(TestTrapAfterMerge_Interp(cond1, x, y), TestTrapAfterMerge_C2(cond1, x, y));
|
|
Asserts.assertEQ(testNestedObjectsObject_Interp(cond1, x, y), testNestedObjectsObject_C2(cond1, x, y));
|
|
Asserts.assertEQ(testNestedObjectsNoEscapeObject_Interp(cond1, x, y), testNestedObjectsNoEscapeObject_C2(cond1, x, y));
|
|
Asserts.assertEQ(testTrappingAfterMerge_Interp(cond1, x, y), testTrappingAfterMerge_C2(cond1, x, y));
|
|
Asserts.assertEQ(testSimpleAliasedAlloc_Interp(cond1, x, y), testSimpleAliasedAlloc_C2(cond1, x, y));
|
|
Asserts.assertEQ(testSimpleDoubleMerge_Interp(cond1, x, y), testSimpleDoubleMerge_C2(cond1, x, y));
|
|
Asserts.assertEQ(testConsecutiveSimpleMerge_Interp(cond1, cond2, x, y), testConsecutiveSimpleMerge_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testDoubleIfElseMerge_Interp(cond1, x, y), testDoubleIfElseMerge_C2(cond1, x, y));
|
|
Asserts.assertEQ(testNoEscapeWithLoadInLoop_Interp(cond1, x, y), testNoEscapeWithLoadInLoop_C2(cond1, x, y));
|
|
Asserts.assertEQ(testCmpAfterMerge_Interp(cond1, cond2, x, y), testCmpAfterMerge_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testCondAfterMergeWithAllocate_Interp(cond1, cond2, x, y), testCondAfterMergeWithAllocate_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testCondLoadAfterMerge_Interp(cond1, cond2, x, y), testCondLoadAfterMerge_C2(cond1, cond2, x, y));
|
|
Asserts.assertEQ(testIfElseInLoop_Interp(), testIfElseInLoop_C2());
|
|
Asserts.assertEQ(testLoadInCondAfterMerge_Interp(cond1, x, y), testLoadInCondAfterMerge_C2(cond1, x, y));
|
|
Asserts.assertEQ(testLoadInLoop_Interp(cond1, x, y), testLoadInLoop_C2(cond1, x, y));
|
|
Asserts.assertEQ(testMergesAndMixedEscape_Interp(cond1, x, y), testMergesAndMixedEscape_C2(cond1, x, y));
|
|
Asserts.assertEQ(testSRAndNSR_NoTrap_Interp(cond1, x, y), testSRAndNSR_NoTrap_C2(cond1, x, y));
|
|
Asserts.assertEQ(testString_one_Interp(cond1), testString_one_C2(cond1));
|
|
Asserts.assertEQ(testString_two_Interp(cond1), testString_two_C2(cond1));
|
|
Asserts.assertEQ(testLoadKlassFromCast_Interp(cond1), testLoadKlassFromCast_C2(cond1));
|
|
Asserts.assertEQ(testLoadKlassFromPhi_Interp(cond1), testLoadKlassFromPhi_C2(cond1));
|
|
Asserts.assertEQ(testReReduce_Interp(cond1, x, y), testReReduce_C2(cond1, x, y));
|
|
|
|
Asserts.assertEQ(testSRAndNSR_Trap_Interp(false, cond1, cond2, x, y),
|
|
testSRAndNSR_Trap_C2(info.isTestC2Compiled("testSRAndNSR_Trap_C2"), cond1, cond2, x, y));
|
|
|
|
var arr1 = testNestedObjectsArray_Interp(cond1, x, y);
|
|
var arr2 = testNestedObjectsArray_C2(cond1, x, y);
|
|
|
|
if (arr1.length != arr2.length) Asserts.fail("testNestedObjectsArray result size mismatch.");
|
|
for (int i=0; i<arr1.length; i++) {
|
|
if (!arr1[i].equals(arr2[i])) {
|
|
Asserts.fail("testNestedObjectsArray objects mismatch.");
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testGlobalEscape(int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
AllocationMergesTests.global_escape = p;
|
|
|
|
return p.x * p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "1" })
|
|
int testGlobalEscape_C2(int x, int y) { return testGlobalEscape(x, y); }
|
|
|
|
@DontCompile
|
|
int testGlobalEscape_Interp(int x, int y) { return testGlobalEscape(x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testArgEscape(int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
int val = dummy(p);
|
|
|
|
return val + p.x + p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "1" })
|
|
int testArgEscape_C2(int x, int y) { return testArgEscape(x, y); }
|
|
|
|
@DontCompile
|
|
int testArgEscape_Interp(int x, int y) { return testArgEscape(x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testEscapeInCallAfterMerge(boolean cond, boolean cond2, int x, int y) {
|
|
Point p = new Point(x, x);
|
|
|
|
if (cond) {
|
|
p = new Point(y, y);
|
|
}
|
|
|
|
if (cond2) {
|
|
dummy(p);
|
|
}
|
|
|
|
return p.x * p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
int testEscapeInCallAfterMerge_C2(boolean cond, boolean cond2, int x, int y) { return testEscapeInCallAfterMerge(cond, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testEscapeInCallAfterMerge_Interp(boolean cond, boolean cond2, int x, int y) { return testEscapeInCallAfterMerge(cond, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testNoEscapeWithWriteInLoop(boolean cond, boolean cond2, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
int res = 0;
|
|
|
|
if (cond) {
|
|
p = new Point(y, x);
|
|
}
|
|
|
|
for (int i=0; i<100; i++) {
|
|
p.x += p.y + i;
|
|
p.y += p.x + i;
|
|
}
|
|
|
|
return res + p.x + p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
// Merge won't be reduced because of the write to the fields
|
|
int testNoEscapeWithWriteInLoop_C2(boolean cond, boolean cond2, int x, int y) { return testNoEscapeWithWriteInLoop(cond, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testNoEscapeWithWriteInLoop_Interp(boolean cond, boolean cond2, int x, int y) { return testNoEscapeWithWriteInLoop(cond, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testPollutedWithWrite(boolean cond, int l) {
|
|
Shape obj1 = new Square(l);
|
|
Shape obj2 = new Square(l);
|
|
Shape obj = null;
|
|
|
|
if (cond) {
|
|
obj = obj1;
|
|
} else {
|
|
obj = obj2;
|
|
}
|
|
|
|
for (int i=1; i<132; i++) {
|
|
obj.x++;
|
|
}
|
|
|
|
return obj1.x + obj2.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
// Merge won't be reduced because of the write to the field
|
|
int testPollutedWithWrite_C2(boolean cond, int l) { return testPollutedWithWrite(cond, l); }
|
|
|
|
@DontCompile
|
|
int testPollutedWithWrite_Interp(boolean cond, int l) { return testPollutedWithWrite(cond, l); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
@ForceInline
|
|
int testPollutedPolymorphic(boolean cond, int l) {
|
|
Shape obj1 = new Square(l);
|
|
Shape obj2 = new Circle(l);
|
|
Shape obj = (cond ? obj1 : obj2);
|
|
int res = 0;
|
|
|
|
for (int i=1; i<232; i++) {
|
|
res += obj.x;
|
|
}
|
|
|
|
return res + obj1.x + obj2.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
int testPollutedPolymorphic_C2(boolean cond, int l) { return testPollutedPolymorphic(cond, l); }
|
|
|
|
@DontCompile
|
|
int testPollutedPolymorphic_Interp(boolean cond, int l) { return testPollutedPolymorphic(cond, l); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testMergedLoadAfterDirectStore(boolean cond, int x, int y) {
|
|
Point p0 = new Point(x, x);
|
|
Point p1 = new Point(y, y);
|
|
Point p = null;
|
|
|
|
if (cond) {
|
|
p = p0;
|
|
} else {
|
|
p = p1;
|
|
}
|
|
|
|
p0.x = x * y;
|
|
|
|
return p.x;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
// Merge won't be reduced because write to one of the inputs *after* the merge
|
|
int testMergedLoadAfterDirectStore_C2(boolean cond, int x, int y) { return testMergedLoadAfterDirectStore(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testMergedLoadAfterDirectStore_Interp(boolean cond, int x, int y) { return testMergedLoadAfterDirectStore(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testMergedAccessAfterCallWithWrite(boolean cond, int x, int y) {
|
|
Point p2 = new Point(x, x);
|
|
Point p = new Point(y, y);
|
|
|
|
p.x = p.x * y;
|
|
|
|
if (cond) {
|
|
p = new Point(x, x);
|
|
}
|
|
|
|
dummy(p2);
|
|
|
|
for (int i=3; i<324; i++) {
|
|
p.x += i * x;
|
|
}
|
|
|
|
return p.x;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "3" })
|
|
// Objects won't be scalar replaced because:
|
|
// - p is written inside the loop.
|
|
// - p2 is ArgEscape
|
|
int testMergedAccessAfterCallWithWrite_C2(boolean cond, int x, int y) { return testMergedAccessAfterCallWithWrite(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testMergedAccessAfterCallWithWrite_Interp(boolean cond, int x, int y) { return testMergedAccessAfterCallWithWrite(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testLoadAfterTrap(boolean cond, int x, int y) {
|
|
Point p = null;
|
|
|
|
if (cond) {
|
|
p = new Point(x, x);
|
|
} else {
|
|
p = new Point(y, y);
|
|
}
|
|
|
|
dummy(x+y);
|
|
|
|
return p.x + p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
// The allocations won't be removed because 'split_through_phi' won't split the load through the bases.
|
|
int testLoadAfterTrap_C2(boolean cond, int x, int y) { return testLoadAfterTrap(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testLoadAfterTrap_Interp(boolean cond, int x, int y) { return testLoadAfterTrap(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCondAfterMergeWithNull(boolean cond1, boolean cond2, int x, int y) {
|
|
Point p = null;
|
|
|
|
if (cond1) {
|
|
p = new Point(y, x);
|
|
}
|
|
|
|
if (cond2 && cond1) {
|
|
return p.x;
|
|
} else {
|
|
return 321;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
int testCondAfterMergeWithNull_C2(boolean cond1, boolean cond2, int x, int y) { return testCondAfterMergeWithNull(cond1, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testCondAfterMergeWithNull_Interp(boolean cond1, boolean cond2, int x, int y) { return testCondAfterMergeWithNull(cond1, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testLoadAfterLoopAlias(int x, int y) {
|
|
Point a = new Point(x, y);
|
|
Point b = new Point(y, x);
|
|
int acc = 0;
|
|
|
|
for (int i=10; i<232; i++) {
|
|
Point c = (i % 2 == 0) ? a : b;
|
|
acc += c.x + c.y;
|
|
}
|
|
|
|
return acc;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations should be removed
|
|
int testLoadAfterLoopAlias_C2(int x, int y) { return testLoadAfterLoopAlias(x, y); }
|
|
|
|
@DontCompile
|
|
int testLoadAfterLoopAlias_Interp(int x, int y) { return testLoadAfterLoopAlias(x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCallTwoSide(boolean cond1, int x, int y) {
|
|
Point p = dummy(x, y);
|
|
|
|
if (cond1) {
|
|
p = dummy(y, x);
|
|
}
|
|
|
|
return (p != null) ? p.x : 0;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.CALL, "<=3" })
|
|
// Merge won't be reduced because both of the inputs are NSR.
|
|
// There could be 3 call nodes because one if can became an unstable trap.
|
|
int testCallTwoSide_C2(boolean cond1, int x, int y) { return testCallTwoSide(cond1, x, y); }
|
|
|
|
@DontCompile
|
|
int testCallTwoSide_Interp(boolean cond1, int x, int y) { return testCallTwoSide(cond1, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testMergedAccessAfterCallNoWrite(boolean cond, int x, int y) {
|
|
Point p2 = new Point(x, x);
|
|
Point p = new Point(y, y);
|
|
int res = 0;
|
|
|
|
p.x = p.x * y;
|
|
|
|
if (cond) {
|
|
p = new Point(y, y);
|
|
}
|
|
|
|
dummy(p2);
|
|
|
|
for (int i=3; i<324; i++) {
|
|
res += p.x + i * x;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "3" })
|
|
// p2 escapes and therefore won't be removed.
|
|
// The allocations won't be removed because 'split_through_phi' won't split the load through the bases.
|
|
int testMergedAccessAfterCallNoWrite_C2(boolean cond, int x, int y) { return testMergedAccessAfterCallNoWrite(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testMergedAccessAfterCallNoWrite_Interp(boolean cond, int x, int y) { return testMergedAccessAfterCallNoWrite(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCmpMergeWithNull_Second(boolean cond, int x, int y) {
|
|
Point p = null;
|
|
|
|
if (cond) {
|
|
p = new Point(x*x, y*y);
|
|
}
|
|
|
|
dummy(x);
|
|
|
|
if (p != null) {
|
|
return p.x * p.y;
|
|
} else {
|
|
return 1984;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "1" })
|
|
int testCmpMergeWithNull_Second_C2(boolean cond, int x, int y) { return testCmpMergeWithNull_Second(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testCmpMergeWithNull_Second_Interp(boolean cond, int x, int y) { return testCmpMergeWithNull_Second(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testObjectIdentity(boolean cond, int x, int y) {
|
|
Point o = new Point(x, y);
|
|
|
|
if (cond && x == 42) {
|
|
o = global_escape;
|
|
}
|
|
|
|
return o.x + o.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
int testObjectIdentity_C2(boolean cond, int x, int y) { return testObjectIdentity(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testObjectIdentity_Interp(boolean cond, int x, int y) { return testObjectIdentity(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testSubclassesTrapping(boolean c1, boolean c2, int x, int y, int w, int z) {
|
|
new A();
|
|
Root s = new Home(x, y);
|
|
new B();
|
|
|
|
if (c1) {
|
|
new C();
|
|
s = new Etc("Hello");
|
|
new D();
|
|
} else {
|
|
new E();
|
|
s = new Usr(y, x, z);
|
|
new F();
|
|
}
|
|
|
|
int res = s.a;
|
|
dummy();
|
|
|
|
return res;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
int testSubclassesTrapping_C2(boolean c1, boolean c2, int x, int y, int w, int z) { return testSubclassesTrapping(c1, c2, x, y, w, z); }
|
|
|
|
@DontCompile
|
|
int testSubclassesTrapping_Interp(boolean c1, boolean c2, int x, int y, int w, int z) { return testSubclassesTrapping(c1, c2, x, y, w, z); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCmpMergeWithNull(boolean cond, int x, int y) {
|
|
Point p = null;
|
|
|
|
if (cond) {
|
|
p = new Point(x*x, y*y);
|
|
} else if (x > y) {
|
|
p = new Point(x+y, x*y);
|
|
}
|
|
|
|
if (p != null) {
|
|
return p.x * p.y;
|
|
} else {
|
|
return 1984;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
int testCmpMergeWithNull_C2(boolean cond, int x, int y) { return testCmpMergeWithNull(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testCmpMergeWithNull_Interp(boolean cond, int x, int y) { return testCmpMergeWithNull(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testSubclasses(boolean c1, boolean c2, int x, int y, int w, int z) {
|
|
new A();
|
|
Root s = new Home(x, y);
|
|
new B();
|
|
|
|
if (c1) {
|
|
new C();
|
|
s = new Etc("Hello");
|
|
new D();
|
|
} else {
|
|
new E();
|
|
s = new Usr(y, x, z);
|
|
new F();
|
|
}
|
|
|
|
new G();
|
|
|
|
return s.a;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
int testSubclasses_C2(boolean c1, boolean c2, int x, int y, int w, int z) { return testSubclasses(c1, c2, x, y, w, z); }
|
|
|
|
@DontCompile
|
|
int testSubclasses_Interp(boolean c1, boolean c2, int x, int y, int w, int z) { return testSubclasses(c1, c2, x, y, w, z); }
|
|
|
|
|
|
// ------------------ Some Scalar Replacement Should Happen in The Tests Below ------------------- //
|
|
|
|
@ForceInline
|
|
int testPartialPhis(boolean cond, int l, int x, int y) {
|
|
int k = l;
|
|
|
|
if (l == 0) {
|
|
k = l + 1;
|
|
} else if (l == 2) {
|
|
k = l + 2;
|
|
} else if (l == 3) {
|
|
new Point(x, y);
|
|
} else if (l == 4) {
|
|
new Point(y, x);
|
|
}
|
|
|
|
return k;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// all allocations will be dead
|
|
int testPartialPhis_C2(boolean cond, int l, int x, int y) { return testPartialPhis(cond, l, x, y); }
|
|
|
|
@DontCompile
|
|
int testPartialPhis_Interp(boolean cond, int l, int x, int y) { return testPartialPhis(cond, l, x, y); }
|
|
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testPollutedNoWrite(boolean cond, int l) {
|
|
Shape obj1 = new Square(l);
|
|
Shape obj2 = new Square(l);
|
|
Shape obj = null;
|
|
int res = 0;
|
|
|
|
if (cond) {
|
|
obj = obj1;
|
|
} else {
|
|
obj = obj2;
|
|
}
|
|
|
|
for (int i=1; i<132; i++) {
|
|
res += obj.x;
|
|
}
|
|
|
|
return res + obj1.x + obj2.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be removed. After initialization they are read-only objects.
|
|
// Access to the input of the merge, after the merge, is fine.
|
|
int testPollutedNoWrite_C2(boolean cond, int l) { return testPollutedNoWrite(cond, l); }
|
|
|
|
@DontCompile
|
|
int testPollutedNoWrite_Interp(boolean cond, int l) { return testPollutedNoWrite(cond, l); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testThreeWayAliasedAlloc(boolean cond, int x, int y) {
|
|
Point p1 = new Point(x, y);
|
|
Point p2 = new Point(x+1, y+1);
|
|
Point p3 = new Point(x+2, y+2);
|
|
|
|
if (cond) {
|
|
p3 = p1;
|
|
} else {
|
|
p3 = p2;
|
|
}
|
|
|
|
return p3.x + p3.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Initial p3 will always be dead.
|
|
// The other two allocations will be reduced and scaled
|
|
int testThreeWayAliasedAlloc_C2(boolean cond, int x, int y) { return testThreeWayAliasedAlloc(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testThreeWayAliasedAlloc_Interp(boolean cond, int x, int y) { return testThreeWayAliasedAlloc(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int TestTrapAfterMerge(boolean cond, int x, int y) {
|
|
Point p = new Point(x, x);
|
|
|
|
if (cond) {
|
|
p = new Point(y, y);
|
|
}
|
|
|
|
for (int i=402; i<432; i+=x) {
|
|
x++;
|
|
}
|
|
|
|
return p.x + x;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be eliminated.
|
|
int TestTrapAfterMerge_C2(boolean cond, int x, int y) { return TestTrapAfterMerge(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int TestTrapAfterMerge_Interp(boolean cond, int x, int y) { return TestTrapAfterMerge(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
Point testNestedObjectsObject(boolean cond, int x, int y) {
|
|
Picture p = new Picture(x, x, y);
|
|
|
|
if (cond) {
|
|
p = new Picture(y, y, x);
|
|
}
|
|
|
|
return p.position;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
// The allocation of "Picture" will be removed and only allocations of "Position" will be kept
|
|
Point testNestedObjectsObject_C2(boolean cond, int x, int y) { return testNestedObjectsObject(cond, x, y); }
|
|
|
|
@DontCompile
|
|
Point testNestedObjectsObject_Interp(boolean cond, int x, int y) { return testNestedObjectsObject(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testNestedObjectsNoEscapeObject(boolean cond, int x, int y) {
|
|
Picture p = new Picture(x, x, y);
|
|
|
|
if (cond) {
|
|
p = new Picture(y, y, x);
|
|
}
|
|
|
|
return p.position.x;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" }, applyIf = {"UseCompressedOops", "true"} )
|
|
@IR(failOn = { IRNode.ALLOC }, applyIf = {"UseCompressedOops", "false"} )
|
|
// The two Picture objects will be removed. The nested Point objects won't
|
|
// be removed, if CompressedOops is enabled, because the Phi merging them will
|
|
// have a DecodeN user - which currently isn't supported.
|
|
int testNestedObjectsNoEscapeObject_C2(boolean cond, int x, int y) { return testNestedObjectsNoEscapeObject(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testNestedObjectsNoEscapeObject_Interp(boolean cond, int x, int y) { return testNestedObjectsNoEscapeObject(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
Point[] testNestedObjectsArray(boolean cond, int x, int y) {
|
|
PicturePositions p = new PicturePositions(x, y, x+y);
|
|
|
|
if (cond) {
|
|
p = new PicturePositions(x+1, y+1, x+y+1);
|
|
}
|
|
|
|
return p.positions;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "4" })
|
|
// The two PicturePositions objects will be reduced and scaled.
|
|
Point[] testNestedObjectsArray_C2(boolean cond, int x, int y) { return testNestedObjectsArray(cond, x, y); }
|
|
|
|
@DontCompile
|
|
Point[] testNestedObjectsArray_Interp(boolean cond, int x, int y) { return testNestedObjectsArray(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testTrappingAfterMerge(boolean cond, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
int res = 0;
|
|
|
|
if (cond) {
|
|
p = new Point(y, y);
|
|
}
|
|
|
|
for (int i=832; i<932; i++) {
|
|
res += p.x;
|
|
}
|
|
|
|
if (x > y) {
|
|
res += new Point(p.x, p.y).x;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// The allocation inside the last if will be removed because it's not part of a merge
|
|
// The other two allocations will be reduced and removed
|
|
int testTrappingAfterMerge_C2(boolean cond, int x, int y) { return testTrappingAfterMerge(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testTrappingAfterMerge_Interp(boolean cond, int x, int y) { return testTrappingAfterMerge(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testSimpleAliasedAlloc(boolean cond, int x, int y) {
|
|
Point p1 = new Point(x, y);
|
|
Point p2 = new Point(y, x);
|
|
Point p = p1;
|
|
|
|
if (cond) {
|
|
p = p2;
|
|
}
|
|
|
|
return p.x * p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both merges will be reduced and removed
|
|
int testSimpleAliasedAlloc_C2(boolean cond, int x, int y) { return testSimpleAliasedAlloc(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testSimpleAliasedAlloc_Interp(boolean cond, int x, int y) { return testSimpleAliasedAlloc(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testSimpleDoubleMerge(boolean cond, int x, int y) {
|
|
Point p1 = new Point(x, y);
|
|
Point p2 = new Point(x+1, y+1);
|
|
|
|
if (cond) {
|
|
p1 = new Point(y, x);
|
|
p2 = new Point(y+1, x+1);
|
|
}
|
|
|
|
return p1.x + p2.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both merges will be reduced and removed
|
|
int testSimpleDoubleMerge_C2(boolean cond, int x, int y) { return testSimpleDoubleMerge(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testSimpleDoubleMerge_Interp(boolean cond, int x, int y) { return testSimpleDoubleMerge(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testConsecutiveSimpleMerge(boolean cond1, boolean cond2, int x, int y) {
|
|
Point p0 = new Point(x, x);
|
|
Point p1 = new Point(x, y);
|
|
Point pA = null;
|
|
|
|
Point p2 = new Point(y, x);
|
|
Point p3 = new Point(y, y);
|
|
Point pB = null;
|
|
|
|
if (cond1) {
|
|
pA = p0;
|
|
} else {
|
|
pA = p1;
|
|
}
|
|
|
|
if (cond2) {
|
|
pB = p2;
|
|
} else {
|
|
pB = p3;
|
|
}
|
|
|
|
return pA.x * pA.y + pB.x * pB.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// All allocations will be removed.
|
|
int testConsecutiveSimpleMerge_C2(boolean cond1, boolean cond2, int x, int y) { return testConsecutiveSimpleMerge(cond1, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testConsecutiveSimpleMerge_Interp(boolean cond1, boolean cond2, int x, int y) { return testConsecutiveSimpleMerge(cond1, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testDoubleIfElseMerge(boolean cond, int x, int y) {
|
|
Point p1 = new Point(x, y);
|
|
Point p2 = new Point(x+1, y+1);
|
|
|
|
if (cond) {
|
|
p1 = new Point(y, x);
|
|
p2 = new Point(y, x);
|
|
} else {
|
|
p1 = new Point(x, y);
|
|
p2 = new Point(x+1, y+1);
|
|
}
|
|
|
|
return p1.x * p2.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// The initial allocation is always dead. The other
|
|
// two will be reduced and scaled.
|
|
int testDoubleIfElseMerge_C2(boolean cond, int x, int y) { return testDoubleIfElseMerge(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testDoubleIfElseMerge_Interp(boolean cond, int x, int y) { return testDoubleIfElseMerge(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testNoEscapeWithLoadInLoop(boolean cond, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
int res = 0;
|
|
|
|
if (cond) {
|
|
p = new Point(y, x);
|
|
}
|
|
|
|
for (int i=3342; i<4234; i++) {
|
|
res += p.x + p.y + i;
|
|
}
|
|
|
|
return res + p.x + p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be reduced and scaled.
|
|
int testNoEscapeWithLoadInLoop_C2(boolean cond, int x, int y) { return testNoEscapeWithLoadInLoop(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testNoEscapeWithLoadInLoop_Interp(boolean cond, int x, int y) { return testNoEscapeWithLoadInLoop(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCmpAfterMerge(boolean cond, boolean cond2, int x, int y) {
|
|
Point a = new Point(x, y);
|
|
Point b = new Point(y, x);
|
|
Point c = null;
|
|
|
|
if (x+2 >= y-5) {
|
|
c = a;
|
|
} else {
|
|
c = b;
|
|
}
|
|
|
|
return cond2 ? c.x : c.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be reduced and scaled
|
|
int testCmpAfterMerge_C2(boolean cond, boolean cond2, int x, int y) { return testCmpAfterMerge(cond, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testCmpAfterMerge_Interp(boolean cond, boolean cond2, int x, int y) { return testCmpAfterMerge(cond, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCondAfterMergeWithAllocate(boolean cond1, boolean cond2, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
if (cond1) {
|
|
p = new Point(y, x);
|
|
}
|
|
|
|
if (cond2 && cond1) {
|
|
return p.x;
|
|
} else {
|
|
return 321;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be eliminated.
|
|
int testCondAfterMergeWithAllocate_C2(boolean cond1, boolean cond2, int x, int y) { return testCondAfterMergeWithAllocate(cond1, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testCondAfterMergeWithAllocate_Interp(boolean cond1, boolean cond2, int x, int y) { return testCondAfterMergeWithAllocate(cond1, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testCondLoadAfterMerge(boolean cond1, boolean cond2, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
if (cond1) {
|
|
p = new Point(y, x);
|
|
}
|
|
|
|
if (cond1 == false && cond2 == false) {
|
|
return p.x + 1;
|
|
} else if (cond1 == false && cond2 == true) {
|
|
return p.x + 30;
|
|
} else if (cond1 == true && cond2 == false) {
|
|
return p.x + 40;
|
|
} else if (cond1 == true && cond2 == true) {
|
|
return p.x + 50;
|
|
} else {
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be eliminated.
|
|
int testCondLoadAfterMerge_C2(boolean cond1, boolean cond2, int x, int y) { return testCondLoadAfterMerge(cond1, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testCondLoadAfterMerge_Interp(boolean cond1, boolean cond2, int x, int y) { return testCondLoadAfterMerge(cond1, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testIfElseInLoop() {
|
|
int res = 0;
|
|
|
|
for (int i=1; i<1000; i++) {
|
|
Point obj = new Point(i, i);
|
|
|
|
if (i % 2 == 1) {
|
|
obj = new Point(i, i+1);
|
|
} else {
|
|
obj = new Point(i-1, i);
|
|
}
|
|
|
|
res += obj.x;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// The initial allocation is always dead. The other
|
|
// two will be reduced and scaled.
|
|
int testIfElseInLoop_C2() { return testIfElseInLoop(); }
|
|
|
|
@DontCompile
|
|
int testIfElseInLoop_Interp() { return testIfElseInLoop(); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testLoadInCondAfterMerge(boolean cond, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
if (cond) {
|
|
p = new Point(y, x);
|
|
}
|
|
|
|
if (p.x == 10) {
|
|
if (p.y == 10) {
|
|
return dummy(10);
|
|
} else {
|
|
return dummy(20);
|
|
}
|
|
} else if (p.x == 20) {
|
|
if (p.y == 20) {
|
|
return dummy(30);
|
|
} else {
|
|
return dummy(40);
|
|
}
|
|
}
|
|
|
|
return 1984;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be reduced and removed.
|
|
int testLoadInCondAfterMerge_C2(boolean cond, int x, int y) { return testLoadInCondAfterMerge(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testLoadInCondAfterMerge_Interp(boolean cond, int x, int y) { return testLoadInCondAfterMerge(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testLoadInLoop(boolean cond, int x, int y) {
|
|
Point obj1 = new Point(x, y);
|
|
Point obj2 = new Point(y, x);
|
|
Point obj = null;
|
|
int res = 0;
|
|
|
|
if (cond) {
|
|
obj = obj1;
|
|
} else {
|
|
obj = obj2;
|
|
}
|
|
|
|
for (int i = 0; i < 532; i++) {
|
|
res += obj.x;
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
// Both allocations will be reduced and removed.
|
|
int testLoadInLoop_C2(boolean cond, int x, int y) { return testLoadInLoop(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testLoadInLoop_Interp(boolean cond, int x, int y) { return testLoadInLoop(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testMergesAndMixedEscape(boolean cond, int x, int y) {
|
|
Point p1 = new Point(x, y);
|
|
Point p2 = new Point(x, y);
|
|
int val = 0;
|
|
|
|
if (cond) {
|
|
p1 = new Point(x+1, y+1);
|
|
val = dummy(p2);
|
|
}
|
|
|
|
return val + p1.x + p2.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "1" })
|
|
// p2 escapes and will remain. The other two allocations will be reduced and scaled.
|
|
int testMergesAndMixedEscape_C2(boolean cond, int x, int y) { return testMergesAndMixedEscape(cond, x, y); }
|
|
|
|
@DontCompile
|
|
int testMergesAndMixedEscape_Interp(boolean cond, int x, int y) { return testMergesAndMixedEscape(cond, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testSRAndNSR_NoTrap(boolean cond1, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
if (cond1) {
|
|
p = new Point(x+1, y+1);
|
|
global_escape = p;
|
|
}
|
|
|
|
return p.y;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "<=1" })
|
|
int testSRAndNSR_NoTrap_C2(boolean cond1, int x, int y) { return testSRAndNSR_NoTrap(cond1, x, y); }
|
|
|
|
@DontCompile
|
|
int testSRAndNSR_NoTrap_Interp(boolean cond1, int x, int y) { return testSRAndNSR_NoTrap(cond1, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testSRAndNSR_Trap(boolean is_c2, boolean cond1, boolean cond2, int x, int y) {
|
|
Point p = new Point(x, y);
|
|
|
|
if (cond1) {
|
|
p = new Point(x+1, y+1);
|
|
global_escape = p;
|
|
}
|
|
|
|
int res = p.x;
|
|
if (is_c2) {
|
|
// This will show up to C2 as a trap.
|
|
dummy_defaults();
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "<=1" })
|
|
int testSRAndNSR_Trap_C2(boolean is_c2, boolean cond1, boolean cond2, int x, int y) { return testSRAndNSR_Trap(is_c2, cond1, cond2, x, y); }
|
|
|
|
@DontCompile
|
|
int testSRAndNSR_Trap_Interp(boolean is_c2, boolean cond1, boolean cond2, int x, int y) { return testSRAndNSR_Trap(is_c2, cond1, cond2, x, y); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
char testString_one(boolean cond1) {
|
|
String p = new String("Java");
|
|
|
|
if (cond1) {
|
|
p = new String("HotSpot");
|
|
}
|
|
|
|
return p.charAt(0);
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
char testString_one_C2(boolean cond1) { return testString_one(cond1); }
|
|
|
|
@DontCompile
|
|
char testString_one_Interp(boolean cond1) { return testString_one(cond1); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
char testString_two(boolean cond1) {
|
|
String p = new String("HotSpot");
|
|
|
|
if (cond1) {
|
|
p = dummy("String");
|
|
if (p == null) return 'J';
|
|
}
|
|
|
|
return p.charAt(0);
|
|
}
|
|
|
|
@Test
|
|
@IR(failOn = { IRNode.ALLOC })
|
|
char testString_two_C2(boolean cond1) { return testString_two(cond1); }
|
|
|
|
@DontCompile
|
|
char testString_two_Interp(boolean cond1) { return testString_two(cond1); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
Class testLoadKlassFromCast(boolean cond1) {
|
|
Object p = new Circle(10);
|
|
|
|
if (cond1) {
|
|
p = dummy(1, 2);
|
|
}
|
|
|
|
return p.getClass();
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "1" })
|
|
// The allocation won't be reduced because we don't support [Narrow]Klass loads
|
|
Class testLoadKlassFromCast_C2(boolean cond1) { return testLoadKlassFromCast(cond1); }
|
|
|
|
@DontCompile
|
|
Class testLoadKlassFromCast_Interp(boolean cond1) { return testLoadKlassFromCast(cond1); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
Class testLoadKlassFromPhi(boolean cond1) {
|
|
Shape p = new Square(20);
|
|
|
|
if (cond1) {
|
|
p = new Circle(10);
|
|
}
|
|
|
|
return p.getClass();
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "2" })
|
|
// The allocation won't be reduced because we don't support [Narrow]Klass loads
|
|
Class testLoadKlassFromPhi_C2(boolean cond1) { return testLoadKlassFromPhi(cond1); }
|
|
|
|
@DontCompile
|
|
Class testLoadKlassFromPhi_Interp(boolean cond1) { return testLoadKlassFromPhi(cond1); }
|
|
|
|
// -------------------------------------------------------------------------
|
|
|
|
@ForceInline
|
|
int testReReduce(boolean cond, int x, int y) {
|
|
Nested A = new Nested(x, y);
|
|
Nested B = new Nested(y, x);
|
|
Nested C = new Nested(y, x);
|
|
Nested P = null;
|
|
|
|
if (x == y) {
|
|
A.other = B;
|
|
P = A;
|
|
} else if (x > y) {
|
|
P = B;
|
|
} else {
|
|
C.other = B;
|
|
P = C;
|
|
}
|
|
|
|
if (x == y)
|
|
dummy_defaults();
|
|
|
|
return P.x;
|
|
}
|
|
|
|
@Test
|
|
@IR(counts = { IRNode.ALLOC, "1" })
|
|
// The last allocation won't be reduced because it would cause the creation
|
|
// of a nested SafePointScalarMergeNode.
|
|
int testReReduce_C2(boolean cond1, int x, int y) { return testReReduce(cond1, x, y); }
|
|
|
|
@DontCompile
|
|
int testReReduce_Interp(boolean cond1, int x, int y) { return testReReduce(cond1, x, y); }
|
|
|
|
// ------------------ Utility for Testing ------------------- //
|
|
|
|
@DontCompile
|
|
static void dummy() {
|
|
}
|
|
|
|
@DontCompile
|
|
static int dummy(Point p) {
|
|
return p.x * p.y;
|
|
}
|
|
|
|
@DontCompile
|
|
static int dummy(int x) {
|
|
return x;
|
|
}
|
|
|
|
@DontCompile
|
|
static Point dummy(int x, int y) {
|
|
return new Point(x, y);
|
|
}
|
|
|
|
@DontCompile
|
|
static String dummy(String str) {
|
|
return str;
|
|
}
|
|
|
|
@DontCompile
|
|
static ADefaults dummy_defaults() {
|
|
return new ADefaults();
|
|
}
|
|
|
|
static class Nested {
|
|
int x, y;
|
|
Nested other;
|
|
Nested(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
this.other = null;
|
|
}
|
|
}
|
|
|
|
static class Point {
|
|
int x, y;
|
|
Point(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
|
|
@Override
|
|
public boolean equals(Object o) {
|
|
if (o == this) return true;
|
|
if (!(o instanceof Point)) return false;
|
|
Point p = (Point) o;
|
|
return (p.x == x) && (p.y == y);
|
|
}
|
|
}
|
|
|
|
class Shape {
|
|
int x, y, l;
|
|
Shape(int x, int y) {
|
|
this.x = x;
|
|
this.y = y;
|
|
}
|
|
}
|
|
|
|
class Square extends Shape {
|
|
Square(int l) {
|
|
super(0, 0);
|
|
this.l = l;
|
|
}
|
|
}
|
|
|
|
class Circle extends Shape {
|
|
Circle(int l) {
|
|
super(0, 0);
|
|
this.l = l;
|
|
}
|
|
}
|
|
|
|
static class ADefaults {
|
|
static int ble;
|
|
int i;
|
|
@DontCompile
|
|
ADefaults(int i) { this.i = i; }
|
|
@DontCompile
|
|
ADefaults() { }
|
|
}
|
|
|
|
static class Picture {
|
|
public int id;
|
|
public Point position;
|
|
|
|
public Picture(int id, int x, int y) {
|
|
this.id = id;
|
|
this.position = new Point(x, y);
|
|
}
|
|
}
|
|
|
|
static class PicturePositions {
|
|
public int id;
|
|
public Point[] positions;
|
|
|
|
public PicturePositions(int id, int x, int y) {
|
|
this.id = id;
|
|
this.positions = new Point[] { new Point(x, y), new Point(y, x) };
|
|
}
|
|
}
|
|
|
|
class Root {
|
|
public int a;
|
|
public int b;
|
|
public int c;
|
|
public int d;
|
|
public int e;
|
|
|
|
public Root(int a, int b, int c, int d, int e) {
|
|
this.a = a;
|
|
this.b = b;
|
|
this.c = c;
|
|
this.d = d;
|
|
this.e = e;
|
|
}
|
|
}
|
|
|
|
class Usr extends Root {
|
|
public float flt;
|
|
|
|
public Usr(float a, float b, float c) {
|
|
super((int)a, (int)b, (int)c, 0, 0);
|
|
this.flt = a;
|
|
}
|
|
}
|
|
|
|
class Home extends Root {
|
|
public double[] arr;
|
|
|
|
public Home(double a, double b) {
|
|
super((int)a, (int)b, 0, 0, 0);
|
|
this.arr = new double[] {a, b};
|
|
}
|
|
|
|
}
|
|
|
|
class Tmp extends Root {
|
|
public String s;
|
|
|
|
public Tmp(String s) {
|
|
super((int)s.length(), 0, 0, 0, 0);
|
|
this.s = s;
|
|
}
|
|
}
|
|
|
|
class Etc extends Root {
|
|
public String a;
|
|
|
|
public Etc(String s) {
|
|
super((int)s.length(), 0, 0, 0, 0);
|
|
this.a = s;
|
|
}
|
|
}
|
|
|
|
class A { }
|
|
class B { }
|
|
class C { }
|
|
class D { }
|
|
class E { }
|
|
class F { }
|
|
class G { }
|
|
}
|