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