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