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