CompilationTask.java revision 13160:721637c92e1e
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.GraalCompilerOptions.ExitVMOnBailout;
26import static org.graalvm.compiler.core.GraalCompilerOptions.ExitVMOnException;
27import static org.graalvm.compiler.core.GraalCompilerOptions.PrintAfterCompilation;
28import static org.graalvm.compiler.core.GraalCompilerOptions.PrintBailout;
29import static org.graalvm.compiler.core.GraalCompilerOptions.PrintCompilation;
30import static org.graalvm.compiler.core.GraalCompilerOptions.PrintFilter;
31import static org.graalvm.compiler.core.GraalCompilerOptions.PrintStackTraceOnException;
32import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
33import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
34
35import java.util.List;
36
37import org.graalvm.compiler.code.CompilationResult;
38import org.graalvm.compiler.debug.Debug;
39import org.graalvm.compiler.debug.Debug.Scope;
40import org.graalvm.compiler.debug.DebugCloseable;
41import org.graalvm.compiler.debug.DebugCounter;
42import org.graalvm.compiler.debug.DebugDumpScope;
43import org.graalvm.compiler.debug.DebugTimer;
44import org.graalvm.compiler.debug.GraalError;
45import org.graalvm.compiler.debug.Management;
46import org.graalvm.compiler.debug.TTY;
47import org.graalvm.compiler.debug.TimeSource;
48import org.graalvm.compiler.options.OptionKey;
49import org.graalvm.compiler.options.OptionValues;
50import org.graalvm.util.EconomicMap;
51
52import jdk.vm.ci.code.BailoutException;
53import jdk.vm.ci.code.CodeCacheProvider;
54import jdk.vm.ci.hotspot.EventProvider;
55import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
56import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
57import jdk.vm.ci.hotspot.HotSpotCompiledCode;
58import jdk.vm.ci.hotspot.HotSpotInstalledCode;
59import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
60import jdk.vm.ci.hotspot.HotSpotNmethod;
61import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
62import jdk.vm.ci.runtime.JVMCICompiler;
63import jdk.vm.ci.services.JVMCIServiceLocator;
64
65public class CompilationTask {
66
67    private static final DebugCounter BAILOUTS = Debug.counter("Bailouts");
68
69    private static final EventProvider eventProvider;
70
71    static {
72        List<EventProvider> providers = JVMCIServiceLocator.getProviders(EventProvider.class);
73        if (providers.size() > 1) {
74            throw new GraalError("Multiple %s providers found: %s", EventProvider.class.getName(), providers);
75        } else if (providers.isEmpty()) {
76            eventProvider = EventProvider.createEmptyEventProvider();
77        } else {
78            eventProvider = providers.get(0);
79        }
80    }
81
82    private final HotSpotJVMCIRuntimeProvider jvmciRuntime;
83
84    private final HotSpotGraalCompiler compiler;
85    private final HotSpotCompilationIdentifier compilationId;
86
87    private HotSpotInstalledCode installedCode;
88
89    /**
90     * Specifies whether the compilation result is installed as the
91     * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method.
92     */
93    private final boolean installAsDefault;
94
95    private final boolean useProfilingInfo;
96    private final OptionValues options;
97
98    final class RetryableCompilation extends HotSpotRetryableCompilation<HotSpotCompilationRequestResult> {
99        private final EventProvider.CompilationEvent compilationEvent;
100        CompilationResult result;
101
102        RetryableCompilation(EventProvider.CompilationEvent compilationEvent) {
103            super(compiler.getGraalRuntime(), options);
104            this.compilationEvent = compilationEvent;
105        }
106
107        @Override
108        public String toString() {
109            return getMethod().format("%H.%n");
110        }
111
112        @SuppressWarnings("try")
113        @Override
114        protected HotSpotCompilationRequestResult run(Throwable retryCause) {
115            HotSpotResolvedJavaMethod method = getMethod();
116            int entryBCI = getEntryBCI();
117            final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
118            CompilationStatistics stats = CompilationStatistics.create(options, method, isOSR);
119            final boolean printCompilation = PrintCompilation.getValue(options) && !TTY.isSuppressed();
120            final boolean printAfterCompilation = PrintAfterCompilation.getValue(options) && !TTY.isSuppressed();
121            if (printCompilation) {
122                TTY.println(getMethodDescription() + "...");
123            }
124
125            TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(options), method);
126            final long start;
127            final long allocatedBytesBefore;
128            if (printAfterCompilation || printCompilation) {
129                final long threadId = Thread.currentThread().getId();
130                start = TimeSource.getTimeNS();
131                allocatedBytesBefore = printAfterCompilation || printCompilation ? Lazy.threadMXBean.getThreadAllocatedBytes(threadId) : 0L;
132            } else {
133                start = 0L;
134                allocatedBytesBefore = 0L;
135            }
136
137            try (Scope s = Debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
138                // Begin the compilation event.
139                compilationEvent.begin();
140                result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options);
141            } catch (Throwable e) {
142                if (retryCause != null) {
143                    log("Exception during retry", e);
144                }
145                throw Debug.handle(e);
146            } finally {
147                // End the compilation event.
148                compilationEvent.end();
149
150                filter.remove();
151
152                if (printAfterCompilation || printCompilation) {
153                    final long threadId = Thread.currentThread().getId();
154                    final long stop = TimeSource.getTimeNS();
155                    final long duration = (stop - start) / 1000000;
156                    final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1;
157                    final int bytecodeSize = result != null ? result.getBytecodeSize() : 0;
158                    final long allocatedBytesAfter = Lazy.threadMXBean.getThreadAllocatedBytes(threadId);
159                    final long allocatedKBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024;
160
161                    if (printAfterCompilation) {
162                        TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dB %5dkB", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
163                    } else if (printCompilation) {
164                        TTY.println(String.format("%-6d JVMCI %-70s %-45s %-50s | %4dms %5dB %5dB %5dkB", getId(), "", "", "", duration, bytecodeSize, targetCodeSize, allocatedKBytes));
165                    }
166                }
167            }
168
169            if (result != null) {
170                try (DebugCloseable b = CodeInstallationTime.start()) {
171                    installMethod(result);
172                }
173            }
174            stats.finish(method, installedCode);
175            if (result != null) {
176                return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize());
177            }
178            return null;
179        }
180    }
181
182    static class Lazy {
183        /**
184         * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the
185         * current compiler thread, e.g. total allocated bytes.
186         */
187        static final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) Management.getThreadMXBean();
188    }
189
190    public CompilationTask(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault,
191                    OptionValues options) {
192        this.jvmciRuntime = jvmciRuntime;
193        this.compiler = compiler;
194        this.compilationId = new HotSpotCompilationIdentifier(request);
195        this.useProfilingInfo = useProfilingInfo;
196        this.installAsDefault = installAsDefault;
197
198        /*
199         * Disable inlining if HotSpot has it disabled unless it's been explicitly set in Graal.
200         */
201        HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
202        GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
203        OptionValues newOptions = options;
204        if (!config.inline) {
205            EconomicMap<OptionKey<?>, Object> m = OptionValues.newOptionMap();
206            if (Inline.getValue(options) && !Inline.hasBeenSet(options)) {
207                m.put(Inline, false);
208            }
209            if (InlineDuringParsing.getValue(options) && !InlineDuringParsing.hasBeenSet(options)) {
210                m.put(InlineDuringParsing, false);
211            }
212            if (!m.isEmpty()) {
213                newOptions = new OptionValues(options, m);
214            }
215        }
216        this.options = newOptions;
217    }
218
219    public HotSpotResolvedJavaMethod getMethod() {
220        return getRequest().getMethod();
221    }
222
223    /**
224     * Returns the compilation id of this task.
225     *
226     * @return compile id
227     */
228    public int getId() {
229        return getRequest().getId();
230    }
231
232    public int getEntryBCI() {
233        return getRequest().getEntryBCI();
234    }
235
236    /**
237     * @return the compilation id plus a trailing '%' is the compilation is an OSR to match
238     *         PrintCompilation style output
239     */
240    public String getIdString() {
241        if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
242            return getId() + "%";
243        } else {
244            return Integer.toString(getId());
245        }
246    }
247
248    public HotSpotInstalledCode getInstalledCode() {
249        return installedCode;
250    }
251
252    /**
253     * Time spent in compilation.
254     */
255    private static final DebugTimer CompilationTime = Debug.timer("CompilationTime");
256
257    /**
258     * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}.
259     */
260    private static final DebugCounter CompiledBytecodes = Debug.counter("CompiledBytecodes");
261
262    /**
263     * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for
264     * which {@linkplain CompilationResult#getTargetCode()} code was installed.
265     */
266    private static final DebugCounter CompiledAndInstalledBytecodes = Debug.counter("CompiledAndInstalledBytecodes");
267
268    /**
269     * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes.
270     */
271    private static final DebugCounter InstalledCodeSize = Debug.counter("InstalledCodeSize");
272
273    /**
274     * Time spent in code installation.
275     */
276    public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation");
277
278    @SuppressWarnings("try")
279    public HotSpotCompilationRequestResult runCompilation() {
280        HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
281        GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
282        int entryBCI = getEntryBCI();
283        boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
284        HotSpotResolvedJavaMethod method = getMethod();
285
286        // register the compilation id in the method metrics
287        if (Debug.isMethodMeterEnabled()) {
288            if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
289                Debug.methodMetrics(method).addToMetric(getId(), "CompilationIdOSR");
290            } else {
291                Debug.methodMetrics(method).addToMetric(getId(), "CompilationId");
292            }
293        }
294
295        // Log a compilation event.
296        EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
297
298        if (installAsDefault) {
299            // If there is already compiled code for this method on our level we simply return.
300            // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we
301            // only need to check for that value.
302            if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
303                return HotSpotCompilationRequestResult.failure("Already compiled", false);
304            }
305        }
306
307        RetryableCompilation compilation = new RetryableCompilation(compilationEvent);
308        try (DebugCloseable a = CompilationTime.start()) {
309            return compilation.execute();
310        } catch (BailoutException bailout) {
311            BAILOUTS.increment();
312            if (ExitVMOnBailout.getValue(options)) {
313                TTY.out.println(method.format("Bailout in %H.%n(%p)"));
314                bailout.printStackTrace(TTY.out);
315                System.exit(-1);
316            } else if (PrintBailout.getValue(options)) {
317                TTY.out.println(method.format("Bailout in %H.%n(%p)"));
318                bailout.printStackTrace(TTY.out);
319            }
320            /*
321             * Handling of permanent bailouts: Permanent bailouts that can happen for example due to
322             * unsupported unstructured control flow in the bytecodes of a method must not be
323             * retried. Hotspot compile broker will ensure that no recompilation at the given tier
324             * will happen if retry is false.
325             */
326            final boolean permanentBailout = bailout.isPermanent();
327            if (permanentBailout && PrintBailout.getValue(options)) {
328                TTY.println("Permanent bailout %s compiling method %s %s.", bailout.getMessage(), HotSpotGraalCompiler.str(method), (isOSR ? "OSR" : ""));
329            }
330            return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !permanentBailout);
331        } catch (Throwable t) {
332            // Log a failure event.
333            EventProvider.CompilerFailureEvent event = eventProvider.newCompilerFailureEvent();
334            if (event.shouldWrite()) {
335                event.setCompileId(getId());
336                event.setMessage(t.getMessage());
337                event.commit();
338            }
339
340            handleException(t);
341            /*
342             * Treat random exceptions from the compiler as indicating a problem compiling this
343             * method. Report the result of toString instead of getMessage to ensure that the
344             * exception type is included in the output in case there's no detail mesage.
345             */
346            return HotSpotCompilationRequestResult.failure(t.toString(), false);
347        } finally {
348            try {
349                int compiledBytecodes = 0;
350                int codeSize = 0;
351
352                if (compilation.result != null) {
353                    compiledBytecodes = compilation.result.getBytecodeSize();
354                    CompiledBytecodes.add(compiledBytecodes);
355                    if (installedCode != null) {
356                        codeSize = installedCode.getSize();
357                        CompiledAndInstalledBytecodes.add(compiledBytecodes);
358                        InstalledCodeSize.add(codeSize);
359                    }
360                }
361
362                // Log a compilation event.
363                if (compilationEvent.shouldWrite()) {
364                    compilationEvent.setMethod(method.format("%H.%n(%p)"));
365                    compilationEvent.setCompileId(getId());
366                    compilationEvent.setCompileLevel(config.compilationLevelFullOptimization);
367                    compilationEvent.setSucceeded(compilation.result != null && installedCode != null);
368                    compilationEvent.setIsOsr(isOSR);
369                    compilationEvent.setCodeSize(codeSize);
370                    compilationEvent.setInlinedBytes(compiledBytecodes);
371                    compilationEvent.commit();
372                }
373            } catch (Throwable t) {
374                handleException(t);
375            }
376        }
377    }
378
379    protected void handleException(Throwable t) {
380        /*
381         * Automatically enable ExitVMOnException during bootstrap or when asserts are enabled but
382         * respect ExitVMOnException if it's been explicitly set.
383         */
384        boolean exitVMOnException = ExitVMOnException.getValue(options);
385        if (!ExitVMOnException.hasBeenSet(options)) {
386            assert (exitVMOnException = true) == true;
387            if (!exitVMOnException) {
388                HotSpotGraalRuntimeProvider runtime = compiler.getGraalRuntime();
389                if (runtime.isBootstrapping()) {
390                    exitVMOnException = true;
391                }
392            }
393        }
394
395        if (PrintStackTraceOnException.getValue(options) || exitVMOnException) {
396            try {
397                t.printStackTrace(TTY.out);
398            } catch (Throwable throwable) {
399                // Don't let an exception here change the other control flow
400            }
401        }
402
403        if (exitVMOnException) {
404            System.exit(-1);
405        }
406    }
407
408    private String getMethodDescription() {
409        HotSpotResolvedJavaMethod method = getMethod();
410        return String.format("%-6d JVMCI %-70s %-45s %-50s %s", getId(), method.getDeclaringClass().getName(), method.getName(), method.getSignature().toMethodDescriptor(),
411                        getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "(OSR@" + getEntryBCI() + ") ");
412    }
413
414    @SuppressWarnings("try")
415    private void installMethod(final CompilationResult compResult) {
416        final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache();
417        installedCode = null;
418        Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult};
419        try (Scope s = Debug.scope("CodeInstall", context)) {
420            HotSpotCompiledCode compiledCode = HotSpotCompiledCodeBuilder.createCompiledCode(codeCache, getRequest().getMethod(), getRequest(), compResult);
421            installedCode = (HotSpotInstalledCode) codeCache.installCode(getRequest().getMethod(), compiledCode, null, getRequest().getMethod().getSpeculationLog(), installAsDefault);
422        } catch (Throwable e) {
423            throw Debug.handle(e);
424        }
425    }
426
427    @Override
428    public String toString() {
429        return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]";
430    }
431
432    private HotSpotCompilationRequest getRequest() {
433        return compilationId.getRequest();
434    }
435}
436