/* * Copyright (c) 2014, 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. */ import sun.hotspot.WhiteBox; import sun.hotspot.code.NMethod; import com.oracle.java.testlibrary.Platform; import java.lang.reflect.Method; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.util.function.BiFunction; /* * @test NullCheckDroppingsTest * @bug 8054492 * @summary "Casting can result in redundant null checks in generated code" * @library /testlibrary /testlibrary/whitebox /testlibrary/com/oracle/java/testlibrary * @build NullCheckDroppingsTest * @run main ClassFileInstaller sun.hotspot.WhiteBox * sun.hotspot.WhiteBox$WhiteBoxPermission * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI * -Xmixed -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:CompileThreshold=1000 * -XX:CompileCommand=exclude,NullCheckDroppingsTest::main NullCheckDroppingsTest */ public class NullCheckDroppingsTest { private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); static final BiFunction fCast = (c, o) -> c.cast(o); static final MethodHandle SET_SSINK; static final MethodHandle MH_CAST; static { try { SET_SSINK = MethodHandles.lookup().findSetter(NullCheckDroppingsTest.class, "ssink", String.class); MH_CAST = MethodHandles.lookup().findVirtual(Class.class, "cast", MethodType.methodType(Object.class, Object.class)); } catch (Exception e) { throw new Error(e); } } static volatile String svalue = "A"; static volatile String snull = null; String ssink; public static void main(String[] args) throws Exception { // Only test C2 in Server VM if (!Platform.isServer()) { return; } // Make sure background compilation is disabled if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { throw new RuntimeException("Background compilation enabled"); } // Make sure Tiered compilation is disabled if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { throw new RuntimeException("Tiered compilation enabled"); } Method testOne = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopOne", String.class); Method testTwo = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopTwo", String.class); Method testThree = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopThree", String.class); Method testFour = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopFour", String.class); NullCheckDroppingsTest t = new NullCheckDroppingsTest(); // Ensure testLoopOne is compiled for (int i = 0; i < 3000; i++) { t.testLoopOne(svalue); t.testLoopTwo(svalue); t.testLoopThree(svalue); t.testLoopFour(svalue); } NMethod nm = getNMethod(testOne); // This should cause a de-optimisation // if testLoopOne is compiled with a null-check. t.testLoopOne(snull); checkDeoptimization(testOne, nm); nm = getNMethod(testTwo); t.testLoopTwo(snull); checkDeoptimization(testTwo, nm); nm = getNMethod(testThree); t.testLoopThree(snull); checkDeoptimization(testThree, nm); nm = getNMethod(testFour); t.testLoopFour(snull); checkDeoptimization(testFour, nm); } void testLoopOne(String s) { try { ssink = String.class.cast(s); } catch (Throwable t) { throw new Error(t); } } void testLoopTwo(String s) { try { ssink = (String) (Object) MH_CAST.invokeExact(String.class, (Object) s);; } catch (Throwable t) { throw new Error(t); } } void testLoopThree(String s) { try { SET_SSINK.invokeExact(this, s); } catch (Throwable t) { throw new Error(t); } } void testLoopFour(String s) { try { ssink = (String) fCast.apply(String.class, s); } catch (Throwable t) { throw new Error(t); } } static NMethod getNMethod(Method test) throws Exception { // Because background compilation is disabled, method should now be compiled if (!WHITE_BOX.isMethodCompiled(test)) { throw new RuntimeException(test + " not compiled"); } NMethod nm = NMethod.get(test, false); // not OSR nmethod if (nm == null) { throw new RuntimeException(test + " missing nmethod?"); } if (nm.comp_level != 4) { throw new RuntimeException(test + " compiled by not C2: " + nm); } return nm; } static void checkDeoptimization(Method test, NMethod nmOrig) throws Exception { // Ensure no deoptimization (intrinsic Class.cast() works). if (!WHITE_BOX.isMethodCompiled(test)) { throw new RuntimeException(test + " deoptimized"); } // Ensure no recompilation. NMethod nm = NMethod.get(test, false); // not OSR nmethod if (nm == null) { throw new RuntimeException(test + " missing nmethod?"); } if (nm.comp_level != 4) { throw new RuntimeException(test + " compiled by not C2: " + nm); } if (nm.compile_id != nmOrig.compile_id) { throw new RuntimeException(test + " was recompiled: old nmethod=" + nmOrig + ", new nmethod=" + nm); } } }