/* * 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 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 { } }