1 /* 2 * Copyright (c) 2012, 2018, 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.hotspot; 26 27 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.Diagnose; 28 import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM; 29 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationBailoutAsFailure; 30 import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction; 31 import static org.graalvm.compiler.core.phases.HighTier.Options.Inline; 32 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing; 33 34 import java.io.PrintStream; 35 36 import jdk.internal.vm.compiler.collections.EconomicMap; 37 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 38 import org.graalvm.compiler.code.CompilationResult; 39 import org.graalvm.compiler.core.CompilationPrinter; 40 import org.graalvm.compiler.core.CompilationWrapper; 41 import org.graalvm.compiler.core.common.CompilationIdentifier; 42 import org.graalvm.compiler.debug.CounterKey; 43 import org.graalvm.compiler.debug.DebugCloseable; 44 import org.graalvm.compiler.debug.DebugContext; 45 import org.graalvm.compiler.debug.DebugDumpScope; 46 import org.graalvm.compiler.debug.TimerKey; 47 import org.graalvm.compiler.options.OptionKey; 48 import org.graalvm.compiler.options.OptionValues; 49 import org.graalvm.compiler.printer.GraalDebugHandlersFactory; 50 51 import jdk.vm.ci.code.BailoutException; 52 import jdk.vm.ci.code.CodeCacheProvider; 53 import jdk.vm.ci.hotspot.HotSpotCompilationRequest; 54 import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult; 55 import jdk.vm.ci.hotspot.HotSpotInstalledCode; 56 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; 57 import jdk.vm.ci.hotspot.HotSpotNmethod; 58 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; 59 import jdk.vm.ci.meta.ResolvedJavaMethod; 60 import jdk.vm.ci.runtime.JVMCICompiler; 61 62 public class CompilationTask { 63 64 private final HotSpotJVMCIRuntime jvmciRuntime; 65 66 private final HotSpotGraalCompiler compiler; 67 private final HotSpotCompilationIdentifier compilationId; 68 69 private HotSpotInstalledCode installedCode; 70 71 /** 72 * Specifies whether the compilation result is installed as the 73 * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method. 74 */ 75 private final boolean installAsDefault; 76 77 private final boolean useProfilingInfo; 78 private final boolean shouldRetainLocalVariables; 79 80 final class HotSpotCompilationWrapper extends CompilationWrapper<HotSpotCompilationRequestResult> { 81 CompilationResult result; 82 83 HotSpotCompilationWrapper() { 84 super(compiler.getGraalRuntime().getOutputDirectory(), compiler.getGraalRuntime().getCompilationProblemsPerAction()); 85 } 86 87 @Override 88 protected DebugContext createRetryDebugContext(OptionValues retryOptions, PrintStream logStream) { 89 SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection(); 90 return DebugContext.create(retryOptions, logStream, new GraalDebugHandlersFactory(snippetReflection)); 91 } 92 93 @Override 94 public String toString() { 95 return getMethod().format("%H.%n(%p)"); 96 } 97 98 @Override 99 protected HotSpotCompilationRequestResult handleException(Throwable t) { 100 if (t instanceof BailoutException) { 101 BailoutException bailout = (BailoutException) t; 102 /* 103 * Handling of permanent bailouts: Permanent bailouts that can happen for example 104 * due to unsupported unstructured control flow in the bytecodes of a method must 105 * not be retried. Hotspot compile broker will ensure that no recompilation at the 106 * given tier will happen if retry is false. 107 */ 108 return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent()); 109 } 110 111 /* 112 * Treat random exceptions from the compiler as indicating a problem compiling this 113 * method. Report the result of toString instead of getMessage to ensure that the 114 * exception type is included in the output in case there's no detail mesage. 115 */ 116 return HotSpotCompilationRequestResult.failure(t.toString(), false); 117 } 118 119 @Override 120 protected ExceptionAction lookupAction(OptionValues values, Throwable cause) { 121 if (cause instanceof BailoutException) { 122 BailoutException bailout = (BailoutException) cause; 123 if (bailout.isPermanent()) { 124 // Respect current action if it has been explicitly set. 125 if (!CompilationBailoutAsFailure.hasBeenSet(values)) { 126 // Get more info for permanent bailouts during bootstrap. 127 if (compiler.getGraalRuntime().isBootstrapping()) { 128 return Diagnose; 129 } 130 131 } 132 } 133 if (!CompilationBailoutAsFailure.getValue(values)) { 134 return super.lookupAction(values, cause); 135 } 136 } 137 138 // Respect current action if it has been explicitly set. 139 if (!CompilationFailureAction.hasBeenSet(values)) { 140 // Automatically exit on failure during bootstrap. 141 if (compiler.getGraalRuntime().isBootstrapping()) { 142 return ExitVM; 143 } 144 } 145 return super.lookupAction(values, cause); 146 } 147 148 @SuppressWarnings("try") 149 @Override 150 protected HotSpotCompilationRequestResult performCompilation(DebugContext debug) { 151 HotSpotResolvedJavaMethod method = getMethod(); 152 int entryBCI = getEntryBCI(); 153 final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 154 CompilationStatistics stats = CompilationStatistics.create(debug.getOptions(), method, isOSR); 155 156 final CompilationPrinter printer = CompilationPrinter.begin(debug.getOptions(), compilationId, method, entryBCI); 157 158 try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) { 159 result = compiler.compile(method, entryBCI, useProfilingInfo, shouldRetainLocalVariables, compilationId, debug); 160 } catch (Throwable e) { 161 throw debug.handle(e); 162 } 163 164 if (result != null) { 165 try (DebugCloseable b = CodeInstallationTime.start(debug)) { 166 installMethod(debug, result); 167 } 168 // Installation is included in compilation time and memory usage reported by printer 169 printer.finish(result); 170 } 171 stats.finish(method, installedCode); 172 if (result != null) { 173 // For compilation of substitutions the method in the compilation request might be 174 // different than the actual method parsed. The root of the compilation will always 175 // be the first method in the methods list, so use that instead. 176 ResolvedJavaMethod rootMethod = result.getMethods()[0]; 177 int inlinedBytecodes = result.getBytecodeSize() - rootMethod.getCodeSize(); 178 assert inlinedBytecodes >= 0 : rootMethod + " " + method; 179 return HotSpotCompilationRequestResult.success(inlinedBytecodes); 180 } 181 return null; 182 } 183 184 } 185 186 public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault) { 187 this(jvmciRuntime, compiler, request, useProfilingInfo, false, installAsDefault); 188 } 189 190 public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean shouldRetainLocalVariables, 191 boolean installAsDefault) { 192 this.jvmciRuntime = jvmciRuntime; 193 this.compiler = compiler; 194 this.compilationId = new HotSpotCompilationIdentifier(request); 195 this.useProfilingInfo = useProfilingInfo; 196 this.shouldRetainLocalVariables = shouldRetainLocalVariables; 197 this.installAsDefault = installAsDefault; 198 } 199 200 public OptionValues filterOptions(OptionValues options) { 201 /* 202 * Disable inlining if HotSpot has it disabled unless it's been explicitly set in Graal. 203 */ 204 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 205 GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); 206 OptionValues newOptions = options; 207 if (!config.inline) { 208 EconomicMap<OptionKey<?>, Object> m = OptionValues.newOptionMap(); 209 if (Inline.getValue(options) && !Inline.hasBeenSet(options)) { 210 m.put(Inline, false); 211 } 212 if (InlineDuringParsing.getValue(options) && !InlineDuringParsing.hasBeenSet(options)) { 213 m.put(InlineDuringParsing, false); 214 } 215 if (!m.isEmpty()) { 216 newOptions = new OptionValues(options, m); 217 } 218 } 219 return newOptions; 220 } 221 222 public HotSpotResolvedJavaMethod getMethod() { 223 return getRequest().getMethod(); 224 } 225 226 CompilationIdentifier getCompilationIdentifier() { 227 return compilationId; 228 } 229 230 /** 231 * Returns the HotSpot id of this compilation. 232 * 233 * @return HotSpot compile id 234 */ 235 public int getId() { 236 return getRequest().getId(); 237 } 238 239 public int getEntryBCI() { 240 return getRequest().getEntryBCI(); 241 } 242 243 /** 244 * @return the compilation id plus a trailing '%' if the compilation is an OSR to match 245 * PrintCompilation style output 246 */ 247 public String getIdString() { 248 if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) { 249 return getId() + "%"; 250 } else { 251 return Integer.toString(getId()); 252 } 253 } 254 255 public HotSpotInstalledCode getInstalledCode() { 256 return installedCode; 257 } 258 259 /** 260 * Time spent in compilation. 261 */ 262 private static final TimerKey CompilationTime = DebugContext.timer("CompilationTime").doc("Time spent in compilation and code installation."); 263 264 /** 265 * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}. 266 */ 267 private static final CounterKey CompiledBytecodes = DebugContext.counter("CompiledBytecodes"); 268 269 /** 270 * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for 271 * which {@linkplain CompilationResult#getTargetCode()} code was installed. 272 */ 273 private static final CounterKey CompiledAndInstalledBytecodes = DebugContext.counter("CompiledAndInstalledBytecodes"); 274 275 /** 276 * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes. 277 */ 278 private static final CounterKey InstalledCodeSize = DebugContext.counter("InstalledCodeSize"); 279 280 /** 281 * Time spent in code installation. 282 */ 283 public static final TimerKey CodeInstallationTime = DebugContext.timer("CodeInstallation"); 284 285 public HotSpotCompilationRequestResult runCompilation(OptionValues initialOptions) { 286 OptionValues options = filterOptions(initialOptions); 287 SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection(); 288 try (DebugContext debug = DebugContext.create(options, new GraalDebugHandlersFactory(snippetReflection))) { 289 return runCompilation(debug); 290 } 291 } 292 293 @SuppressWarnings("try") 294 public HotSpotCompilationRequestResult runCompilation(DebugContext debug) { 295 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 296 GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); 297 int entryBCI = getEntryBCI(); 298 boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 299 HotSpotResolvedJavaMethod method = getMethod(); 300 301 if (installAsDefault || isOSR) { 302 // If there is already compiled code for this method on our level we simply return. 303 // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we 304 // only need to check for that value. 305 if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) { 306 return HotSpotCompilationRequestResult.failure("Already compiled", false); 307 } 308 if (HotSpotGraalCompilerFactory.shouldExclude(method)) { 309 return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false); 310 } 311 } 312 313 HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper(); 314 try (DebugCloseable a = CompilationTime.start(debug)) { 315 return compilation.run(debug); 316 } finally { 317 try { 318 int compiledBytecodes = 0; 319 int codeSize = 0; 320 321 if (compilation.result != null) { 322 compiledBytecodes = compilation.result.getBytecodeSize(); 323 CompiledBytecodes.add(debug, compiledBytecodes); 324 if (installedCode != null) { 325 codeSize = installedCode.getSize(); 326 CompiledAndInstalledBytecodes.add(debug, compiledBytecodes); 327 InstalledCodeSize.add(debug, codeSize); 328 } 329 } 330 } catch (Throwable t) { 331 return compilation.handleException(t); 332 } 333 } 334 } 335 336 @SuppressWarnings("try") 337 private void installMethod(DebugContext debug, final CompilationResult compResult) { 338 final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache(); 339 HotSpotBackend backend = compiler.getGraalRuntime().getHostBackend(); 340 installedCode = null; 341 Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult}; 342 try (DebugContext.Scope s = debug.scope("CodeInstall", context)) { 343 HotSpotCompilationRequest request = getRequest(); 344 installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, 345 request.getMethod(), 346 request, 347 compResult, 348 null, 349 installAsDefault, 350 context); 351 } catch (Throwable e) { 352 throw debug.handle(e); 353 } 354 } 355 356 @Override 357 public String toString() { 358 return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]"; 359 } 360 361 private HotSpotCompilationRequest getRequest() { 362 return compilationId.getRequest(); 363 } 364 }