1 /* 2 * Copyright (c) 2012, 2020, 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 36 import jdk.internal.vm.compiler.collections.EconomicMap; 37 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 38 import org.graalvm.compiler.code.CompilationResult; 39 import org.graalvm.compiler.core.CompilationPrinter; 40 import org.graalvm.compiler.core.CompilationWrapper; 41 import org.graalvm.compiler.core.common.CompilationIdentifier; 42 import org.graalvm.compiler.debug.CounterKey; 43 import org.graalvm.compiler.debug.DebugCloseable; 44 import org.graalvm.compiler.debug.DebugContext; 45 import org.graalvm.compiler.debug.DebugContext.Builder; 46 import org.graalvm.compiler.debug.DebugContext.Description; 47 import org.graalvm.compiler.debug.DebugDumpScope; 48 import org.graalvm.compiler.debug.DebugHandlersFactory; 49 import org.graalvm.compiler.debug.TimerKey; 50 import org.graalvm.compiler.nodes.StructuredGraph; 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.HotSpotCompilationRequest; 58 import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult; 59 import jdk.vm.ci.hotspot.HotSpotInstalledCode; 60 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime; 61 import jdk.vm.ci.hotspot.HotSpotNmethod; 62 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod; 63 import jdk.vm.ci.meta.ResolvedJavaMethod; 64 import jdk.vm.ci.runtime.JVMCICompiler; 65 66 public class CompilationTask { 67 68 private final HotSpotJVMCIRuntime jvmciRuntime; 69 70 private final HotSpotGraalCompiler compiler; 71 private final HotSpotCompilationIdentifier compilationId; 72 73 private HotSpotInstalledCode installedCode; 74 75 /** 76 * Specifies whether the compilation result is installed as the 77 * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method. 78 */ 79 private final boolean installAsDefault; 80 81 private final boolean useProfilingInfo; 82 private final boolean shouldRetainLocalVariables; 83 84 final class HotSpotCompilationWrapper extends CompilationWrapper<HotSpotCompilationRequestResult> { 85 CompilationResult result; 86 HotSpotCompilationWrapper()87 HotSpotCompilationWrapper() { 88 super(compiler.getGraalRuntime().getOutputDirectory(), compiler.getGraalRuntime().getCompilationProblemsPerAction()); 89 } 90 91 @Override createRetryDebugContext(DebugContext initialDebug, OptionValues retryOptions, PrintStream logStream)92 protected DebugContext createRetryDebugContext(DebugContext initialDebug, OptionValues retryOptions, PrintStream logStream) { 93 SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection(); 94 Description description = initialDebug.getDescription(); 95 DebugHandlersFactory factory = new GraalDebugHandlersFactory(snippetReflection); 96 return new Builder(retryOptions, factory).globalMetrics(initialDebug.getGlobalMetrics()).description(description).logStream(logStream).build(); 97 } 98 99 @Override exitHostVM(int status)100 protected void exitHostVM(int status) { 101 HotSpotGraalServices.exit(status); 102 } 103 104 @Override toString()105 public String toString() { 106 return getMethod().format("%H.%n(%p) @ " + getEntryBCI()); 107 } 108 109 @Override handleException(Throwable t)110 protected HotSpotCompilationRequestResult handleException(Throwable t) { 111 if (t instanceof BailoutException) { 112 BailoutException bailout = (BailoutException) t; 113 /* 114 * Handling of permanent bailouts: Permanent bailouts that can happen for example 115 * due to unsupported unstructured control flow in the bytecodes of a method must 116 * not be retried. Hotspot compile broker will ensure that no recompilation at the 117 * given tier will happen if retry is false. 118 */ 119 return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent()); 120 } 121 122 /* 123 * Treat random exceptions from the compiler as indicating a problem compiling this 124 * method. Report the result of toString instead of getMessage to ensure that the 125 * exception type is included in the output in case there's no detail mesage. 126 */ 127 return HotSpotCompilationRequestResult.failure(t.toString(), false); 128 } 129 130 @Override lookupAction(OptionValues values, Throwable cause)131 protected ExceptionAction lookupAction(OptionValues values, Throwable cause) { 132 if (cause instanceof BailoutException) { 133 BailoutException bailout = (BailoutException) cause; 134 if (bailout.isPermanent()) { 135 // Respect current action if it has been explicitly set. 136 if (!CompilationBailoutAsFailure.hasBeenSet(values)) { 137 // Get more info for permanent bailouts during bootstrap. 138 if (compiler.getGraalRuntime().isBootstrapping()) { 139 return Diagnose; 140 } 141 142 } 143 } 144 if (!CompilationBailoutAsFailure.getValue(values)) { 145 return super.lookupAction(values, cause); 146 } 147 } 148 149 // Respect current action if it has been explicitly set. 150 if (!CompilationFailureAction.hasBeenSet(values)) { 151 // Automatically exit on failure during bootstrap. 152 if (compiler.getGraalRuntime().isBootstrapping()) { 153 return ExitVM; 154 } 155 } 156 return super.lookupAction(values, cause); 157 } 158 159 @SuppressWarnings("try") 160 @Override performCompilation(DebugContext debug)161 protected HotSpotCompilationRequestResult performCompilation(DebugContext debug) { 162 HotSpotResolvedJavaMethod method = getMethod(); 163 int entryBCI = getEntryBCI(); 164 final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 165 CompilationStatistics stats = CompilationStatistics.create(debug.getOptions(), method, isOSR); 166 167 final CompilationPrinter printer = CompilationPrinter.begin(debug.getOptions(), compilationId, method, entryBCI); 168 169 StructuredGraph graph; 170 try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) { 171 graph = compiler.createGraph(method, entryBCI, useProfilingInfo, compilationId, debug.getOptions(), debug); 172 result = compiler.compile(graph, method, entryBCI, useProfilingInfo, shouldRetainLocalVariables, compilationId, debug); 173 } catch (Throwable e) { 174 throw debug.handle(e); 175 } 176 177 if (result != null) { 178 try (DebugCloseable b = CodeInstallationTime.start(debug)) { 179 installMethod(debug, graph, 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 // For compilation of substitutions the method in the compilation request might be 187 // different than the actual method parsed. The root of the compilation will always 188 // be the first method in the methods list, so use that instead. 189 ResolvedJavaMethod rootMethod = result.getMethods()[0]; 190 int inlinedBytecodes = result.getBytecodeSize() - rootMethod.getCodeSize(); 191 assert inlinedBytecodes >= 0 : rootMethod + " " + method; 192 return HotSpotCompilationRequestResult.success(inlinedBytecodes); 193 } 194 return null; 195 } 196 197 } 198 CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault)199 public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, 200 HotSpotGraalCompiler compiler, 201 HotSpotCompilationRequest request, 202 boolean useProfilingInfo, 203 boolean installAsDefault) { 204 this(jvmciRuntime, compiler, request, useProfilingInfo, false, installAsDefault); 205 } 206 CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean shouldRetainLocalVariables, boolean installAsDefault)207 public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime, 208 HotSpotGraalCompiler compiler, 209 HotSpotCompilationRequest request, 210 boolean useProfilingInfo, 211 boolean shouldRetainLocalVariables, 212 boolean installAsDefault) { 213 this.jvmciRuntime = jvmciRuntime; 214 this.compiler = compiler; 215 this.compilationId = new HotSpotCompilationIdentifier(request); 216 this.useProfilingInfo = useProfilingInfo; 217 this.shouldRetainLocalVariables = shouldRetainLocalVariables; 218 this.installAsDefault = installAsDefault; 219 } 220 filterOptions(OptionValues options)221 public OptionValues filterOptions(OptionValues options) { 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 return newOptions; 241 } 242 getMethod()243 public HotSpotResolvedJavaMethod getMethod() { 244 return getRequest().getMethod(); 245 } 246 getCompilationIdentifier()247 CompilationIdentifier getCompilationIdentifier() { 248 return compilationId; 249 } 250 251 /** 252 * Returns the HotSpot id of this compilation. 253 * 254 * @return HotSpot compile id 255 */ getId()256 public int getId() { 257 return getRequest().getId(); 258 } 259 getEntryBCI()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 */ getIdString()268 public String getIdString() { 269 if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) { 270 return getId() + "%"; 271 } else { 272 return Integer.toString(getId()); 273 } 274 } 275 getInstalledCode()276 public HotSpotInstalledCode getInstalledCode() { 277 return installedCode; 278 } 279 280 /** 281 * Time spent in compilation. 282 */ 283 public 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 public 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 runCompilation(OptionValues initialOptions)306 public HotSpotCompilationRequestResult runCompilation(OptionValues initialOptions) { 307 OptionValues options = filterOptions(initialOptions); 308 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 309 try (DebugContext debug = graalRuntime.openDebugContext(options, compilationId, getMethod(), compiler.getDebugHandlersFactories(), DebugContext.getDefaultLogStream())) { 310 return runCompilation(debug); 311 } 312 } 313 314 @SuppressWarnings("try") runCompilation(DebugContext debug)315 public HotSpotCompilationRequestResult runCompilation(DebugContext debug) { 316 HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime(); 317 GraalHotSpotVMConfig config = graalRuntime.getVMConfig(); 318 int entryBCI = getEntryBCI(); 319 boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; 320 HotSpotResolvedJavaMethod method = getMethod(); 321 322 if (installAsDefault || isOSR) { 323 // If there is already compiled code for this method on our level we simply return. 324 // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we 325 // only need to check for that value. 326 if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) { 327 return HotSpotCompilationRequestResult.failure("Already compiled", false); 328 } 329 if (HotSpotGraalCompilerFactory.shouldExclude(method)) { 330 return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false); 331 } 332 } 333 334 HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper(); 335 try (DebugCloseable a = CompilationTime.start(debug)) { 336 return compilation.run(debug); 337 } finally { 338 try { 339 int compiledBytecodes = 0; 340 int codeSize = 0; 341 342 if (compilation.result != null) { 343 compiledBytecodes = compilation.result.getBytecodeSize(); 344 CompiledBytecodes.add(debug, compiledBytecodes); 345 if (installedCode != null) { 346 codeSize = installedCode.getSize(); 347 CompiledAndInstalledBytecodes.add(debug, compiledBytecodes); 348 InstalledCodeSize.add(debug, codeSize); 349 } 350 } 351 } catch (Throwable t) { 352 return compilation.handleException(t); 353 } 354 } 355 } 356 357 @SuppressWarnings("try") installMethod(DebugContext debug, StructuredGraph graph, final CompilationResult compResult)358 private void installMethod(DebugContext debug, StructuredGraph graph, final CompilationResult compResult) { 359 final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache(); 360 HotSpotBackend backend = compiler.getGraalRuntime().getHostBackend(); 361 installedCode = null; 362 Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult}; 363 try (DebugContext.Scope s = debug.scope("CodeInstall", context, graph)) { 364 HotSpotCompilationRequest request = getRequest(); 365 installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, 366 request.getMethod(), 367 request, 368 compResult, 369 null, 370 installAsDefault, 371 context); 372 } catch (Throwable e) { 373 throw debug.handle(e); 374 } 375 } 376 377 @Override toString()378 public String toString() { 379 return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]"; 380 } 381 getRequest()382 private HotSpotCompilationRequest getRequest() { 383 return compilationId.getRequest(); 384 } 385 } 386