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 */
23package org.graalvm.compiler.hotspot;
24
25import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM;
26import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
27import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
28import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
29
30import java.util.List;
31
32import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
33import org.graalvm.compiler.code.CompilationResult;
34import org.graalvm.compiler.core.CompilationPrinter;
35import org.graalvm.compiler.core.CompilationWrapper;
36import org.graalvm.compiler.core.common.CompilationIdentifier;
37import org.graalvm.compiler.debug.Assertions;
38import org.graalvm.compiler.debug.CounterKey;
39import org.graalvm.compiler.debug.DebugCloseable;
40import org.graalvm.compiler.debug.DebugContext;
41import org.graalvm.compiler.debug.DebugDumpScope;
42import org.graalvm.compiler.debug.GraalError;
43import org.graalvm.compiler.debug.TimerKey;
44import org.graalvm.compiler.options.EnumOptionKey;
45import org.graalvm.compiler.options.OptionKey;
46import org.graalvm.compiler.options.OptionValues;
47import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
48import org.graalvm.util.EconomicMap;
49
50import jdk.vm.ci.code.BailoutException;
51import jdk.vm.ci.code.CodeCacheProvider;
52import jdk.vm.ci.hotspot.EventProvider;
53import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
54import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
55import jdk.vm.ci.hotspot.HotSpotInstalledCode;
56import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
57import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
58import jdk.vm.ci.hotspot.HotSpotNmethod;
59import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
60import jdk.vm.ci.runtime.JVMCICompiler;
61import jdk.vm.ci.services.JVMCIServiceLocator;
62
63public 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(), compiler.getGraalRuntime().getCompilationProblemsPerAction());
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}
373