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