1 /* 2 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 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 */ 23 24 import sun.hotspot.WhiteBox; 25 import sun.hotspot.code.NMethod; 26 import com.oracle.java.testlibrary.Platform; 27 28 import java.lang.reflect.Method; 29 import java.lang.invoke.MethodHandle; 30 import java.lang.invoke.MethodHandles; 31 import java.lang.invoke.MethodType; 32 import java.util.function.BiFunction; 33 34 /* 35 * @test NullCheckDroppingsTest 36 * @bug 8054492 37 * @summary "Casting can result in redundant null checks in generated code" 38 * @library /testlibrary /testlibrary/whitebox /testlibrary/com/oracle/java/testlibrary 39 * @build NullCheckDroppingsTest 40 * @run main ClassFileInstaller sun.hotspot.WhiteBox 41 * sun.hotspot.WhiteBox$WhiteBoxPermission 42 * @run main ClassFileInstaller com.oracle.java.testlibrary.Platform 43 * @run main/othervm -Xbootclasspath/a:. -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI 44 * -Xmixed -XX:-BackgroundCompilation -XX:-TieredCompilation -XX:CompileThreshold=1000 45 * -XX:CompileCommand=exclude,NullCheckDroppingsTest::main NullCheckDroppingsTest 46 */ 47 48 public class NullCheckDroppingsTest { 49 50 private static final WhiteBox WHITE_BOX = WhiteBox.getWhiteBox(); 51 52 static final BiFunction<Class, Object, Object> fCast = (c, o) -> c.cast(o); 53 54 static final MethodHandle SET_SSINK; 55 static final MethodHandle MH_CAST; 56 57 static { 58 try { 59 SET_SSINK = MethodHandles.lookup().findSetter(NullCheckDroppingsTest.class, "ssink", String.class); 60 MH_CAST = MethodHandles.lookup().findVirtual(Class.class, 61 "cast", 62 MethodType.methodType(Object.class, Object.class)); 63 } 64 catch (Exception e) { 65 throw new Error(e); 66 } 67 } 68 69 static volatile String svalue = "A"; 70 static volatile String snull = null; 71 72 String ssink; 73 74 public static void main(String[] args) throws Exception { 75 76 // Only test C2 in Server VM 77 if (!Platform.isServer()) { 78 return; 79 } 80 // Make sure background compilation is disabled 81 if (WHITE_BOX.getBooleanVMFlag("BackgroundCompilation")) { 82 throw new RuntimeException("Background compilation enabled"); 83 } 84 // Make sure Tiered compilation is disabled 85 if (WHITE_BOX.getBooleanVMFlag("TieredCompilation")) { 86 throw new RuntimeException("Tiered compilation enabled"); 87 } 88 89 Method testOne = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopOne", String.class); 90 Method testTwo = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopTwo", String.class); 91 Method testThree = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopThree", String.class); 92 Method testFour = NullCheckDroppingsTest.class.getDeclaredMethod("testLoopFour", String.class); 93 94 NullCheckDroppingsTest t = new NullCheckDroppingsTest(); 95 96 // Ensure testLoopOne is compiled 97 for (int i = 0; i < 3000; i++) { 98 t.testLoopOne(svalue); 99 t.testLoopTwo(svalue); 100 t.testLoopThree(svalue); 101 t.testLoopFour(svalue); 102 } 103 NMethod nm = getNMethod(testOne); 104 // This should cause a de-optimisation 105 // if testLoopOne is compiled with a null-check. 106 t.testLoopOne(snull); 107 checkDeoptimization(testOne, nm); 108 109 nm = getNMethod(testTwo); 110 t.testLoopTwo(snull); 111 checkDeoptimization(testTwo, nm); 112 113 nm = getNMethod(testThree); 114 t.testLoopThree(snull); 115 checkDeoptimization(testThree, nm); 116 117 nm = getNMethod(testFour); 118 t.testLoopFour(snull); 119 checkDeoptimization(testFour, nm); 120 } 121 122 void testLoopOne(String s) { 123 try { 124 ssink = String.class.cast(s); 125 } catch (Throwable t) { 126 throw new Error(t); 127 } 128 } 129 130 void testLoopTwo(String s) { 131 try { 132 ssink = (String) (Object) MH_CAST.invokeExact(String.class, (Object) s);; 133 } catch (Throwable t) { 134 throw new Error(t); 135 } 136 } 137 138 void testLoopThree(String s) { 139 try { 140 SET_SSINK.invokeExact(this, s); 141 } catch (Throwable t) { 142 throw new Error(t); 143 } 144 } 145 146 void testLoopFour(String s) { 147 try { 148 ssink = (String) fCast.apply(String.class, s); 149 } catch (Throwable t) { 150 throw new Error(t); 151 } 152 } 153 154 static NMethod getNMethod(Method test) throws Exception { 155 // Because background compilation is disabled, method should now be compiled 156 if (!WHITE_BOX.isMethodCompiled(test)) { 157 throw new RuntimeException(test + " not compiled"); 158 } 159 160 NMethod nm = NMethod.get(test, false); // not OSR nmethod 161 if (nm == null) { 162 throw new RuntimeException(test + " missing nmethod?"); 163 } 164 if (nm.comp_level != 4) { 165 throw new RuntimeException(test + " compiled by not C2: " + nm); 166 } 167 return nm; 168 } 169 170 static void checkDeoptimization(Method test, NMethod nmOrig) throws Exception { 171 // Ensure no deoptimization (intrinsic Class.cast() works). 172 if (!WHITE_BOX.isMethodCompiled(test)) { 173 throw new RuntimeException(test + " deoptimized"); 174 } 175 176 // Ensure no recompilation. 177 NMethod nm = NMethod.get(test, false); // not OSR nmethod 178 if (nm == null) { 179 throw new RuntimeException(test + " missing nmethod?"); 180 } 181 if (nm.comp_level != 4) { 182 throw new RuntimeException(test + " compiled by not C2: " + nm); 183 } 184 if (nm.compile_id != nmOrig.compile_id) { 185 throw new RuntimeException(test + " was recompiled: old nmethod=" + nmOrig + ", new nmethod=" + nm); 186 } 187 } 188 }