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