1 /* 2 * Copyright (c) 2014, 2017, 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 /* 25 * @test 26 * @bug 8030976 8059226 27 * @requires !vm.graal.enabled 28 * @library /test/lib / 29 * @modules java.base/jdk.internal.org.objectweb.asm 30 * java.base/jdk.internal.misc 31 * java.compiler 32 * java.management 33 * jdk.internal.jvmstat/sun.jvmstat.monitor 34 * 35 * @build sun.hotspot.WhiteBox 36 * @run driver ClassFileInstaller sun.hotspot.WhiteBox 37 * sun.hotspot.WhiteBox$WhiteBoxPermission 38 * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 39 * -XX:+WhiteBoxAPI -XX:+LogCompilation 40 * -XX:CompileCommand=compileonly,UnstableIfExecutable.test 41 * -XX:LogFile=always_taken_not_fired.xml 42 * compiler.uncommontrap.TestUnstableIfTrap ALWAYS_TAKEN false 43 * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 44 * -XX:+WhiteBoxAPI -XX:+LogCompilation 45 * -XX:CompileCommand=compileonly,UnstableIfExecutable.test 46 * -XX:LogFile=always_taken_fired.xml 47 * compiler.uncommontrap.TestUnstableIfTrap ALWAYS_TAKEN true 48 * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 49 * -XX:+WhiteBoxAPI -XX:+LogCompilation 50 * -XX:CompileCommand=compileonly,UnstableIfExecutable.test 51 * -XX:LogFile=never_taken_not_fired.xml 52 * compiler.uncommontrap.TestUnstableIfTrap NEVER_TAKEN false 53 * @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions 54 * -XX:+WhiteBoxAPI -XX:+LogCompilation 55 * -XX:CompileCommand=compileonly,UnstableIfExecutable.test 56 * -XX:LogFile=never_taken_fired.xml 57 * compiler.uncommontrap.TestUnstableIfTrap NEVER_TAKEN true 58 * @run driver compiler.testlibrary.uncommontrap.Verifier always_taken_not_fired.xml 59 * always_taken_fired.xml 60 * never_taken_not_fired.xml 61 * never_taken_fired.xml 62 */ 63 64 package compiler.uncommontrap; 65 66 import compiler.testlibrary.uncommontrap.Verifier; 67 import jdk.internal.org.objectweb.asm.ClassVisitor; 68 import jdk.internal.org.objectweb.asm.ClassWriter; 69 import jdk.internal.org.objectweb.asm.Label; 70 import jdk.internal.org.objectweb.asm.MethodVisitor; 71 import jdk.test.lib.ByteCodeLoader; 72 import jdk.test.lib.Platform; 73 import sun.hotspot.WhiteBox; 74 75 import java.io.File; 76 import java.io.FileWriter; 77 import java.io.IOException; 78 import java.lang.reflect.Method; 79 import java.util.Properties; 80 81 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_ABSTRACT; 82 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_PUBLIC; 83 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_STATIC; 84 import static jdk.internal.org.objectweb.asm.Opcodes.ACC_VOLATILE; 85 import static jdk.internal.org.objectweb.asm.Opcodes.GETSTATIC; 86 import static jdk.internal.org.objectweb.asm.Opcodes.GOTO; 87 import static jdk.internal.org.objectweb.asm.Opcodes.IADD; 88 import static jdk.internal.org.objectweb.asm.Opcodes.ICONST_1; 89 import static jdk.internal.org.objectweb.asm.Opcodes.IFEQ; 90 import static jdk.internal.org.objectweb.asm.Opcodes.ILOAD; 91 import static jdk.internal.org.objectweb.asm.Opcodes.ISUB; 92 import static jdk.internal.org.objectweb.asm.Opcodes.RETURN; 93 94 public class TestUnstableIfTrap { 95 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 96 private static final String CLASS_NAME = "UnstableIfExecutable"; 97 private static final String METHOD_NAME = "test"; 98 private static final String FIELD_NAME = "field"; 99 private static final int ITERATIONS = 1_000_000; 100 // There is no dependency on particular class file version, so it could be 101 // set to any version (if you're updating this test for Java 42). 102 private static final int CLASS_FILE_VERSION = 49; 103 private static final int MAX_TIER = 4; 104 // This test aimed to verify that uncommon trap with reason "unstable_if" 105 // is emitted when method that contain control-flow divergence such that 106 // one of two branches is never taken (and other one is taken always). 107 // C2 will made a decision whether or not the branch was ever taken 108 // depending on method's profile. 109 // If profile was collected for a few method's invocations, then C2 will not 110 // trust in branches' probabilities and the tested trap won't be emitted. 111 // In fact, a method has to be invoked at least 40 time at the day when this 112 // comment was written (see Parse::dynamic_branch_prediction for an actual 113 // value). It would be to implementation dependent to use "40" as 114 // a threshold value in the test, so in order to improve test's robustness 115 // the threshold value is 1000: if the tested method was compiled by C2 116 // before it was invoked 1000 times, then we won't verify that trap was 117 // emitted and fired. 118 private static final int MIN_INVOCATIONS_BEFORE_C2_COMPILATION = 1000; 119 /** 120 * Description of test case parameters and uncommon trap that will 121 * be emitted during tested method compilation. 122 */ 123 private static enum TestCaseName { 124 ALWAYS_TAKEN(false, "taken always"), 125 NEVER_TAKEN(true, "taken never"); TestCaseName(boolean predicate, String comment)126 TestCaseName(boolean predicate, String comment) { 127 this.predicate = predicate; 128 this.comment = comment; 129 } 130 131 public final boolean predicate; 132 public final String name = "unstable_if"; 133 public final String comment; 134 } 135 main(String args[])136 public static void main(String args[]) { 137 if (args.length != 2) { 138 throw new Error("Expected two arguments: test case name and a " 139 + "boolean determining if uncommon trap should be fired."); 140 } 141 test(TestCaseName.valueOf(args[0]), Boolean.valueOf(args[1])); 142 } 143 test(TestCaseName testCase, boolean shouldBeFired)144 private static void test(TestCaseName testCase, boolean shouldBeFired) { 145 Method testMethod; 146 Label unstableIfLocation = new Label(); 147 boolean shouldBeEmitted; 148 boolean compiledToEarly = false; 149 150 try { 151 Class testClass = ByteCodeLoader.load(CLASS_NAME, 152 generateTest(unstableIfLocation)); 153 testMethod = testClass.getDeclaredMethod(METHOD_NAME, 154 boolean.class); 155 for (int i = 0; i < ITERATIONS; i++) { 156 testMethod.invoke(null, testCase.predicate); 157 if (i < MIN_INVOCATIONS_BEFORE_C2_COMPILATION 158 && isMethodCompiledByC2(testMethod)) { 159 compiledToEarly = true; 160 // There is no sense in further invocations: we already 161 // decided to avoid verification. 162 break; 163 } 164 } 165 // We're checking that trap should be emitted (i.e. it was compiled 166 // by C2) before the trap is fired, because otherwise the nmethod 167 // will be deoptimized and isMethodCompiledByC2 will return false. 168 shouldBeEmitted = isMethodCompiledByC2(testMethod) 169 && !compiledToEarly; 170 if (shouldBeFired) { 171 testMethod.invoke(null, !testCase.predicate); 172 } 173 } catch (ReflectiveOperationException e) { 174 throw new Error("Test case should be generated, loaded and executed" 175 + " without any issues.", e); 176 } 177 178 shouldBeFired &= shouldBeEmitted; 179 180 Properties properties = new Properties(); 181 properties.setProperty(Verifier.VERIFICATION_SHOULD_BE_SKIPPED, 182 Boolean.toString(compiledToEarly)); 183 properties.setProperty(Verifier.UNCOMMON_TRAP_SHOULD_EMITTED, 184 Boolean.toString(shouldBeEmitted)); 185 properties.setProperty(Verifier.UNCOMMON_TRAP_SHOULD_FIRED, 186 Boolean.toString(shouldBeFired)); 187 properties.setProperty(Verifier.UNCOMMON_TRAP_NAME, testCase.name); 188 properties.setProperty(Verifier.UNCOMMON_TRAP_COMMENT, 189 testCase.comment); 190 properties.setProperty(Verifier.UNCOMMON_TRAP_BCI, 191 Integer.toString(unstableIfLocation.getOffset())); 192 193 properties.list(System.out); 194 195 File f = new File(WB.getStringVMFlag("LogFile") + 196 Verifier.PROPERTIES_FILE_SUFFIX); 197 try (FileWriter wr = new FileWriter(f)) { 198 properties.store(wr, ""); 199 } catch (IOException e) { 200 throw new Error("Unable to store test properties.", e); 201 } 202 } 203 isMethodCompiledByC2(Method m)204 private static boolean isMethodCompiledByC2(Method m) { 205 boolean isTiered = WB.getBooleanVMFlag("TieredCompilation"); 206 boolean isMethodCompiled = WB.isMethodCompiled(m); 207 boolean isMethodCompiledAtMaxTier 208 = WB.getMethodCompilationLevel(m) == MAX_TIER; 209 210 return Platform.isServer() && !Platform.isEmulatedClient() && isMethodCompiled 211 && (!isTiered || isMethodCompiledAtMaxTier); 212 } 213 214 /** 215 * Generates class with name {@code CLASS_NAME}, which will contain a 216 * static method {@code METHOD_NAME}: 217 * 218 * <pre>{@code 219 * public abstract class UnstableIfExecutable { 220 * private static int field = 0; 221 * 222 * public static void test(boolean alwaysTrue) { 223 * if (alwaysTrue) { 224 * field++; 225 * } else { 226 * field--; 227 * } 228 * } 229 * } 230 * }</pre> 231 * 232 * @return generated bytecode. 233 */ generateTest(Label unstableIfLocation)234 private static byte[] generateTest(Label unstableIfLocation) { 235 ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES); 236 237 cw.visit(CLASS_FILE_VERSION, ACC_PUBLIC | ACC_ABSTRACT, CLASS_NAME, 238 null, "java/lang/Object", null); 239 240 cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_VOLATILE, FIELD_NAME, 241 "I", null, Integer.valueOf(0)); 242 243 generateTestMethod(cw, unstableIfLocation); 244 245 return cw.toByteArray(); 246 } 247 generateTestMethod(ClassVisitor cv, Label unstableIfLocation)248 private static void generateTestMethod(ClassVisitor cv, 249 Label unstableIfLocation) { 250 MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, METHOD_NAME, 251 "(Z)V", null, null); 252 mv.visitCode(); 253 254 Label end = new Label(); 255 Label falseBranch = new Label(); 256 257 // push "field" field's value and 1 to stack 258 mv.visitFieldInsn(GETSTATIC, CLASS_NAME, FIELD_NAME, "I"); 259 mv.visitInsn(ICONST_1); 260 // load argument's value 261 mv.visitVarInsn(ILOAD, 0); // alwaysTrue 262 // here is our unstable if 263 mv.visitLabel(unstableIfLocation); 264 mv.visitJumpInsn(IFEQ, falseBranch); 265 // increment on "true" 266 mv.visitInsn(IADD); 267 mv.visitJumpInsn(GOTO, end); 268 // decrement on "false" 269 mv.visitLabel(falseBranch); 270 mv.visitInsn(ISUB); 271 mv.visitLabel(end); 272 // bye bye 273 mv.visitInsn(RETURN); 274 275 mv.visitMaxs(0, 0); 276 mv.visitEnd(); 277 } 278 } 279 280