1 /* 2 * Copyright (c) 2011, 2019, 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 package org.graalvm.compiler.replacements.test; 26 27 import java.util.Random; 28 29 import org.graalvm.compiler.api.directives.GraalDirectives; 30 import org.graalvm.compiler.core.phases.HighTier; 31 import org.graalvm.compiler.core.test.GraalCompilerTest; 32 import org.graalvm.compiler.nodes.ValueNode; 33 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; 34 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; 35 import org.graalvm.compiler.options.OptionValues; 36 import org.graalvm.compiler.phases.common.AbstractInliningPhase; 37 import org.graalvm.compiler.serviceprovider.JavaVersionUtil; 38 import org.graalvm.compiler.test.ExportingClassLoader; 39 import org.junit.Assert; 40 import org.junit.Assume; 41 import org.junit.Test; 42 import org.objectweb.asm.ClassWriter; 43 import org.objectweb.asm.Label; 44 import org.objectweb.asm.MethodVisitor; 45 import org.objectweb.asm.Opcodes; 46 47 import jdk.vm.ci.code.InstalledCode; 48 import jdk.vm.ci.meta.DeoptimizationReason; 49 import jdk.vm.ci.meta.ResolvedJavaMethod; 50 51 /** 52 * Tests that deoptimization upon exception handling works. 53 */ 54 public class DeoptimizeOnExceptionTest extends GraalCompilerTest { 55 56 public DeoptimizeOnExceptionTest() { 57 createSuites(getInitialOptions()).getHighTier().findPhase(AbstractInliningPhase.class).remove(); 58 } 59 60 private static void raiseException(String m1, String m2, String m3, String m4, String m5) { 61 throw new RuntimeException(m1 + m2 + m3 + m4 + m5); 62 } 63 64 @Test 65 public void test1() { 66 test("test1Snippet", "m1", "m2", "m3", "m4", "m5"); 67 } 68 69 // no local exception handler - will deopt 70 public static String test1Snippet(String m1, String m2, String m3, String m4, String m5) { 71 if (m1 != null) { 72 raiseException(m1, m2, m3, m4, m5); 73 } 74 return m1 + m2 + m3 + m4 + m5; 75 } 76 77 @Test 78 public void test2() { 79 test("test2Snippet"); 80 } 81 82 public String test2Snippet() throws Exception { 83 try { 84 ClassLoader testCl = new MyClassLoader(); 85 @SuppressWarnings("unchecked") 86 Class<Runnable> c = (Class<Runnable>) testCl.loadClass(name); 87 Runnable r = c.getDeclaredConstructor().newInstance(); 88 ct = Long.MAX_VALUE; 89 // warmup 90 for (int i = 0; i < 100; i++) { 91 r.run(); 92 } 93 // compile 94 ResolvedJavaMethod method = getResolvedJavaMethod(c, "run"); 95 getCode(method); 96 ct = 0; 97 r.run(); 98 } catch (Throwable e) { 99 e.printStackTrace(System.out); 100 Assert.fail(); 101 } 102 return "SUCCESS"; 103 } 104 105 @Test 106 public void test3() { 107 Assume.assumeTrue("Only works on jdk8 right now", JavaVersionUtil.JAVA_SPEC <= 8); 108 ResolvedJavaMethod method = getResolvedJavaMethod("test3Snippet"); 109 110 for (int i = 0; i < 2; i++) { 111 Result actual; 112 boolean expectedCompiledCode = (method.getProfilingInfo().getDeoptimizationCount(DeoptimizationReason.NotCompiledExceptionHandler) != 0); 113 InstalledCode code = getCode(method, null, false, true, new OptionValues(getInitialOptions(), HighTier.Options.Inline, false)); 114 assertTrue(code.isValid()); 115 116 try { 117 actual = new Result(code.executeVarargs(false), null); 118 } catch (Exception e) { 119 actual = new Result(null, e); 120 } 121 122 assertTrue(i > 0 == expectedCompiledCode, "expect compiled code to stay around after the first iteration"); 123 assertEquals(new Result(expectedCompiledCode, null), actual); 124 assertTrue(expectedCompiledCode == code.isValid()); 125 } 126 } 127 128 @Override 129 protected InlineInvokePlugin.InlineInfo bytecodeParserShouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) { 130 if (method.getName().equals("throwException")) { 131 if (b.getMethod().getProfilingInfo().getDeoptimizationCount(DeoptimizationReason.NotCompiledExceptionHandler) != 0) { 132 return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_WITH_EXCEPTION; 133 } else { 134 return InlineInvokePlugin.InlineInfo.DO_NOT_INLINE_NO_EXCEPTION; 135 } 136 } 137 return super.bytecodeParserShouldInlineInvoke(b, method, args); 138 } 139 140 private static void throwException() throws Exception { 141 throw new Exception(); 142 } 143 144 static int footprint; 145 146 public static boolean test3Snippet(boolean rethrowException) throws Exception { 147 try { 148 footprint = 1; 149 throwException(); 150 } catch (Exception e) { 151 footprint = 2; 152 if (rethrowException) { 153 throw e; 154 } 155 } 156 157 return GraalDirectives.inCompiledCode(); 158 } 159 160 public static class MyClassLoader extends ExportingClassLoader { 161 @Override 162 protected Class<?> findClass(String className) throws ClassNotFoundException { 163 return defineClass(name.replace('/', '.'), clazz, 0, clazz.length); 164 } 165 } 166 167 public static void methodB() { 168 Random r = new Random(System.currentTimeMillis()); 169 while (r.nextFloat() > .03f) { 170 // Empty 171 } 172 173 return; 174 } 175 176 public static void methodA() { 177 Random r = new Random(System.currentTimeMillis()); 178 while (r.nextDouble() > .05) { 179 // Empty 180 } 181 return; 182 } 183 184 private static Object m = new Object(); 185 static long ct = Long.MAX_VALUE; 186 187 public static Object getM() { 188 if (ct-- > 0) { 189 return m; 190 } else { 191 return null; 192 } 193 } 194 195 private static String name = "t/TestJSR"; 196 197 private static final byte[] clazz = makeClazz(); 198 199 private static byte[] makeClazz() { 200 // Code generated the class below using asm. 201 String clazzName = DeoptimizeOnExceptionTest.class.getName().replace('.', '/'); 202 final ClassWriter w = new ClassWriter(0); 203 w.visit(Opcodes.V1_5, Opcodes.ACC_PUBLIC, 204 "t/TestJSR", null, "java/lang/Object", 205 new String[]{"java/lang/Runnable"}); 206 MethodVisitor mv = w.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, new String[]{}); 207 mv.visitCode(); 208 mv.visitVarInsn(Opcodes.ALOAD, 0); 209 mv.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false); 210 mv.visitInsn(Opcodes.RETURN); 211 mv.visitMaxs(10, 10); 212 mv.visitEnd(); 213 214 mv = w.visitMethod(Opcodes.ACC_PUBLIC, "run", "()V", null, null); 215 mv.visitCode(); 216 mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazzName, "getM", "()Ljava/lang/Object;", false); 217 Label l1 = new Label(); 218 mv.visitJumpInsn(Opcodes.JSR, l1); 219 mv.visitInsn(Opcodes.RETURN); 220 221 mv.visitLabel(l1); 222 mv.visitVarInsn(Opcodes.ASTORE, 1); 223 224 Label lElse = new Label(); 225 Label lEnd = new Label(); 226 mv.visitMethodInsn(Opcodes.INVOKESTATIC, "java/lang/System", "currentTimeMillis", "()J", false); 227 mv.visitInsn(Opcodes.POP2); 228 mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazzName, "getM", "()Ljava/lang/Object;", false); 229 mv.visitInsn(Opcodes.DUP); 230 mv.visitJumpInsn(Opcodes.IFNULL, lElse); 231 mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazzName, "methodA", "()V", false); 232 mv.visitJumpInsn(Opcodes.GOTO, lEnd); 233 mv.visitLabel(lElse); 234 mv.visitMethodInsn(Opcodes.INVOKESTATIC, clazzName, "methodB", "()V", false); 235 mv.visitLabel(lEnd); 236 237 mv.visitVarInsn(Opcodes.RET, 1); 238 mv.visitMaxs(10, 10); 239 mv.visitEnd(); 240 return w.toByteArray(); 241 } 242 }