CompilationTask.java revision 13160:721637c92e1e
1/* 2 * Copyright (c) 2012, 2016, 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 */ 23package org.graalvm.compiler.hotspot; 24 25import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnBailout; 26import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnException; 27import static org.graalvm.compiler.core.GraalCompilerOptions.PrintAfterCompilation; 28import static org.graalvm.compiler.core.GraalCompilerOptions.PrintBailout; 29import static org.graalvm.compiler.core.GraalCompilerOptions.PrintCompilation; 30import static org.graalvm.compiler.core.GraalCompilerOptions.PrintFilter; 31import static org.graalvm.compiler.core.GraalCompilerOptions.PrintStackTraceOnException; 32import static org.graalvm.compiler.core.phases.HighTier.Options.Inline; 33import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing; 34 35import java.util.List; 36 37import org.graalvm.compiler.code.CompilationResult; 38import org.graalvm.compiler.debug.Debug; 39import org.graalvm.compiler.debug.Debug.Scope; 40import org.graalvm.compiler.debug.DebugCloseable; 41import org.graalvm.compiler.debug.DebugCounter; 42import org.graalvm.compiler.debug.DebugDumpScope; 43import org.graalvm.compiler.debug.DebugTimer; 44import org.graalvm.compiler.debug.GraalError; 45import org.graalvm.compiler.debug.Management; 46import org.graalvm.compiler.debug.TTY; 47import org.graalvm.compiler.debug.TimeSource; 48import org.graalvm.compiler.options.OptionKey; 49import org.graalvm.compiler.options.OptionValues; 50import org.graalvm.util.EconomicMap; 51 52import jdk.vm.ci.code.BailoutException; 53import jdk.vm.ci.code.CodeCacheProvider; 54import jdk.vm.ci.hotspot.EventProvider; 55import jdk.vm.ci.hotspot.HotSpotCompilationRequest; 56import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult; 57import jdk.vm.ci.hotspot.HotSpotCompiledCode; 58import jdk.vm.ci.hotspot.HotSpotInstalledCode; 59import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider; 60import jdk.vm.ci.hotspot.HotSpotNmethod; 61import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; 62import jdk.vm.ci.runtime.JVMCICompiler; 63import jdk.vm.ci.services.JVMCIServiceLocator; 64 65public class CompilationTask { 66 67 private static final DebugCounter BAILOUTS = Debug.counter("Bailouts"); 68 69 private static final EventProvider eventProvider; 70 71 static { 72 List<EventProvider> providers = JVMCIServiceLocator.getProviders(EventProvider.class); 73 if (providers.size() > 1) { 74 throw new GraalError("Multiple %s providers found: %s", EventProvider.class.getName(), providers); 75 } else if (providers.isEmpty()) { 76 eventProvider = EventProvider.createEmptyEventProvider(); 77 } else { 78 eventProvider = providers.get(0); 79 } 80 } 81 82 private final HotSpotJVMCIRuntimeProvider jvmciRuntime; 83 84 private final HotSpotGraalCompiler compiler; 85 private final HotSpotCompilationIdentifier compilationId; 86 87 private HotSpotInstalledCode installedCode; 88 89 /** 90 * Specifies whether the compilation result is installed as the 91 * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method. 92 */ 93 private final boolean installAsDefault; 94 95 private final boolean useProfilingInfo; 96 private final OptionValues options; 97 98 final class RetryableCompilation extends HotSpotRetryableCompilation<HotSpotCompilationRequestResult> { 99 private final EventProvider.CompilationEvent compilationEvent; 100 CompilationResult result; 101 102 RetryableCompilation(EventProvider.CompilationEvent compilationEvent) { 103 super(compiler.getGraalRuntime(), options); 104 this.compilationEvent = compilationEvent; 105 } 106 107 @Override 108 public String toString() { 109 return getMethod().format("%H.%n"); 110 } 111 112 @SuppressWarnings("try") 113 @Override 114 protected HotSpotCompilationRequestResult run(Throwable retryCause) { 115 HotSpotResolvedJavaMethod method = getMethod(); 116 int entryBCI = getEntryBCI(); 117 final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 118 CompilationStatistics stats = CompilationStatistics.create(options, method, isOSR); 119 final boolean printCompilation = PrintCompilation.getValue(options) && !TTY.isSuppressed(); 120 final boolean printAfterCompilation = PrintAfterCompilation.getValue(options) && !TTY.isSuppressed(); 121 if (printCompilation) { 122 TTY.println(getMethodDescription() + "..."); 123 } 124 125 TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(options), method); 126 final long start; 127 final long allocatedBytesBefore; 128 if (printAfterCompilation || printCompilation) { 129 final long threadId = Thread.currentThread().getId(); 130 start = TimeSource.getTimeNS(); 131 allocatedBytesBefore = printAfterCompilation || printCompilation ? Lazy.threadMXBean.getThreadAllocatedBytes(threadId) : 0L; 132 } else { 133 start = 0L; 134 allocatedBytesBefore = 0L; 135 } 136 137 try (Scope s = Debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) { 138 // Begin the compilation event. 139 compilationEvent.begin(); 140 result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options); 141 } catch (Throwable e) { 142 if (retryCause != null) { 143 log("Exception during retry", e); 144 } 145 throw Debug.handle(e); 146 } finally { 147 // End the compilation event. 148 compilationEvent.end(); 149 150 filter.remove(); 151 152 if (printAfterCompilation || printCompilation) { 153 final long threadId = Thread.currentThread().getId(); 154 final long stop = TimeSource.getTimeNS(); 155 final long duration = (stop - start) / 1000000; 156 final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1; 157 final int bytecodeSize = result != null ? result.getBytecodeSize() : 0; 158 final long allocatedBytesAfter = Lazy.threadMXBean.getThreadAllocatedBytes(threadId); 159 final long allocatedKBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024; 160 161 if (printAfterCompilation) { 162 TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dB %5dkB", duration, bytecodeSize, targetCodeSize, allocatedKBytes)); 163 } else if (printCompilation) { 164 TTY.println(String.format("%-6d JVMCI %-70s %-45s %-50s | %4dms %5dB %5dB %5dkB", getId(), "", "", "", duration, bytecodeSize, targetCodeSize, allocatedKBytes)); 165 } 166 } 167 } 168 169 if (result != null) { 170 try (DebugCloseable b = CodeInstallationTime.start()) { 171 installMethod(result); 172 } 173 } 174 stats.finish(method, installedCode); 175 if (result != null) { 176 return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize()); 177 } 178 return null; 179 } 180 } 181 182 static class Lazy { 183 /** 184 * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the 185 * current compiler thread, e.g. total allocated bytes. 186 */ 187 static final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) Management.getThreadMXBean(); 188 } 189 190 public CompilationTask(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault, 191 OptionValues options) { 192 this.jvmciRuntime = jvmciRuntime; 193 this.compiler = compiler; 194 this.compilationId = new HotSpotCompilationIdentifier(request); 195 this.useProfilingInfo = useProfilingInfo; 196 this.installAsDefault = installAsDefault; 197 198 /* 199 * Disable inlining if HotSpot has it disabled unless it's been explicitly set in Graal. 200 */ 201 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 202 GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); 203 OptionValues newOptions = options; 204 if (!config.inline) { 205 EconomicMap<OptionKey<?>, Object> m = OptionValues.newOptionMap(); 206 if (Inline.getValue(options) && !Inline.hasBeenSet(options)) { 207 m.put(Inline, false); 208 } 209 if (InlineDuringParsing.getValue(options) && !InlineDuringParsing.hasBeenSet(options)) { 210 m.put(InlineDuringParsing, false); 211 } 212 if (!m.isEmpty()) { 213 newOptions = new OptionValues(options, m); 214 } 215 } 216 this.options = newOptions; 217 } 218 219 public HotSpotResolvedJavaMethod getMethod() { 220 return getRequest().getMethod(); 221 } 222 223 /** 224 * Returns the compilation id of this task. 225 * 226 * @return compile id 227 */ 228 public int getId() { 229 return getRequest().getId(); 230 } 231 232 public int getEntryBCI() { 233 return getRequest().getEntryBCI(); 234 } 235 236 /** 237 * @return the compilation id plus a trailing '%' is the compilation is an OSR to match 238 * PrintCompilation style output 239 */ 240 public String getIdString() { 241 if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) { 242 return getId() + "%"; 243 } else { 244 return Integer.toString(getId()); 245 } 246 } 247 248 public HotSpotInstalledCode getInstalledCode() { 249 return installedCode; 250 } 251 252 /** 253 * Time spent in compilation. 254 */ 255 private static final DebugTimer CompilationTime = Debug.timer("CompilationTime"); 256 257 /** 258 * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}. 259 */ 260 private static final DebugCounter CompiledBytecodes = Debug.counter("CompiledBytecodes"); 261 262 /** 263 * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for 264 * which {@linkplain CompilationResult#getTargetCode()} code was installed. 265 */ 266 private static final DebugCounter CompiledAndInstalledBytecodes = Debug.counter("CompiledAndInstalledBytecodes"); 267 268 /** 269 * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes. 270 */ 271 private static final DebugCounter InstalledCodeSize = Debug.counter("InstalledCodeSize"); 272 273 /** 274 * Time spent in code installation. 275 */ 276 public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation"); 277 278 @SuppressWarnings("try") 279 public HotSpotCompilationRequestResult runCompilation() { 280 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 281 GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); 282 int entryBCI = getEntryBCI(); 283 boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 284 HotSpotResolvedJavaMethod method = getMethod(); 285 286 // register the compilation id in the method metrics 287 if (Debug.isMethodMeterEnabled()) { 288 if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) { 289 Debug.methodMetrics(method).addToMetric(getId(), "CompilationIdOSR"); 290 } else { 291 Debug.methodMetrics(method).addToMetric(getId(), "CompilationId"); 292 } 293 } 294 295 // Log a compilation event. 296 EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent(); 297 298 if (installAsDefault) { 299 // If there is already compiled code for this method on our level we simply return. 300 // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we 301 // only need to check for that value. 302 if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) { 303 return HotSpotCompilationRequestResult.failure("Already compiled", false); 304 } 305 } 306 307 RetryableCompilation compilation = new RetryableCompilation(compilationEvent); 308 try (DebugCloseable a = CompilationTime.start()) { 309 return compilation.execute(); 310 } catch (BailoutException bailout) { 311 BAILOUTS.increment(); 312 if (ExitVMOnBailout.getValue(options)) { 313 TTY.out.println(method.format("Bailout in %H.%n(%p)")); 314 bailout.printStackTrace(TTY.out); 315 System.exit(-1); 316 } else if (PrintBailout.getValue(options)) { 317 TTY.out.println(method.format("Bailout in %H.%n(%p)")); 318 bailout.printStackTrace(TTY.out); 319 } 320 /* 321 * Handling of permanent bailouts: Permanent bailouts that can happen for example due to 322 * unsupported unstructured control flow in the bytecodes of a method must not be 323 * retried. Hotspot compile broker will ensure that no recompilation at the given tier 324 * will happen if retry is false. 325 */ 326 final boolean permanentBailout = bailout.isPermanent(); 327 if (permanentBailout && PrintBailout.getValue(options)) { 328 TTY.println("Permanent bailout %s compiling method %s %s.", bailout.getMessage(), HotSpotGraalCompiler.str(method), (isOSR ? "OSR" : "")); 329 } 330 return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !permanentBailout); 331 } catch (Throwable t) { 332 // Log a failure event. 333 EventProvider.CompilerFailureEvent event = eventProvider.newCompilerFailureEvent(); 334 if (event.shouldWrite()) { 335 event.setCompileId(getId()); 336 event.setMessage(t.getMessage()); 337 event.commit(); 338 } 339 340 handleException(t); 341 /* 342 * Treat random exceptions from the compiler as indicating a problem compiling this 343 * method. Report the result of toString instead of getMessage to ensure that the 344 * exception type is included in the output in case there's no detail mesage. 345 */ 346 return HotSpotCompilationRequestResult.failure(t.toString(), false); 347 } finally { 348 try { 349 int compiledBytecodes = 0; 350 int codeSize = 0; 351 352 if (compilation.result != null) { 353 compiledBytecodes = compilation.result.getBytecodeSize(); 354 CompiledBytecodes.add(compiledBytecodes); 355 if (installedCode != null) { 356 codeSize = installedCode.getSize(); 357 CompiledAndInstalledBytecodes.add(compiledBytecodes); 358 InstalledCodeSize.add(codeSize); 359 } 360 } 361 362 // Log a compilation event. 363 if (compilationEvent.shouldWrite()) { 364 compilationEvent.setMethod(method.format("%H.%n(%p)")); 365 compilationEvent.setCompileId(getId()); 366 compilationEvent.setCompileLevel(config.compilationLevelFullOptimization); 367 compilationEvent.setSucceeded(compilation.result != null && installedCode != null); 368 compilationEvent.setIsOsr(isOSR); 369 compilationEvent.setCodeSize(codeSize); 370 compilationEvent.setInlinedBytes(compiledBytecodes); 371 compilationEvent.commit(); 372 } 373 } catch (Throwable t) { 374 handleException(t); 375 } 376 } 377 } 378 379 protected void handleException(Throwable t) { 380 /* 381 * Automatically enable ExitVMOnException during bootstrap or when asserts are enabled but 382 * respect ExitVMOnException if it's been explicitly set. 383 */ 384 boolean exitVMOnException = ExitVMOnException.getValue(options); 385 if (!ExitVMOnException.hasBeenSet(options)) { 386 assert (exitVMOnException = true) == true; 387 if (!exitVMOnException) { 388 HotSpotGraalRuntimeProvider runtime = compiler.getGraalRuntime(); 389 if (runtime.isBootstrapping()) { 390 exitVMOnException = true; 391 } 392 } 393 } 394 395 if (PrintStackTraceOnException.getValue(options) || exitVMOnException) { 396 try { 397 t.printStackTrace(TTY.out); 398 } catch (Throwable throwable) { 399 // Don't let an exception here change the other control flow 400 } 401 } 402 403 if (exitVMOnException) { 404 System.exit(-1); 405 } 406 } 407 408 private String getMethodDescription() { 409 HotSpotResolvedJavaMethod method = getMethod(); 410 return String.format("%-6d JVMCI %-70s %-45s %-50s %s", getId(), method.getDeclaringClass().getName(), method.getName(), method.getSignature().toMethodDescriptor(), 411 getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "(OSR@" + getEntryBCI() + ") "); 412 } 413 414 @SuppressWarnings("try") 415 private void installMethod(final CompilationResult compResult) { 416 final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache(); 417 installedCode = null; 418 Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult}; 419 try (Scope s = Debug.scope("CodeInstall", context)) { 420 HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, getRequest().getMethod(), getRequest(), compResult); 421 installedCode = (HotSpotInstalledCode) codeCache.installCode(getRequest().getMethod(), compiledCode, null, getRequest().getMethod().getSpeculationLog(), installAsDefault); 422 } catch (Throwable e) { 423 throw Debug.handle(e); 424 } 425 } 426 427 @Override 428 public String toString() { 429 return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]"; 430 } 431 432 private HotSpotCompilationRequest getRequest() { 433 return compilationId.getRequest(); 434 } 435} 436