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