--- /dev/null 2014-10-29 11:45:38.000000000 -0700 +++ new/test/compiler/intrinsics/classcast/NullCheckDroppingsTest.java 2014-10-29 11:45:38.000000000 -0700 @@ -0,0 +1,188 @@ +/* + * 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); + } + } +}