src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java
changeset 47216 71c04702a3d5
parent 46807 8b2c620d7092
child 47667 390896759aa2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.internal.vm.compiler/share/classes/org.graalvm.compiler.hotspot/src/org/graalvm/compiler/hotspot/CompilationTask.java	Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package org.graalvm.compiler.hotspot;
+
+import static org.graalvm.compiler.core.CompilationWrapper.ExceptionAction.ExitVM;
+import static org.graalvm.compiler.core.GraalCompilerOptions.CompilationFailureAction;
+import static org.graalvm.compiler.core.phases.HighTier.Options.Inline;
+import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
+
+import java.util.List;
+
+import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
+import org.graalvm.compiler.code.CompilationResult;
+import org.graalvm.compiler.core.CompilationPrinter;
+import org.graalvm.compiler.core.CompilationWrapper;
+import org.graalvm.compiler.core.common.CompilationIdentifier;
+import org.graalvm.compiler.debug.Assertions;
+import org.graalvm.compiler.debug.CounterKey;
+import org.graalvm.compiler.debug.DebugCloseable;
+import org.graalvm.compiler.debug.DebugContext;
+import org.graalvm.compiler.debug.DebugDumpScope;
+import org.graalvm.compiler.debug.GraalError;
+import org.graalvm.compiler.debug.TimerKey;
+import org.graalvm.compiler.options.EnumOptionKey;
+import org.graalvm.compiler.options.OptionKey;
+import org.graalvm.compiler.options.OptionValues;
+import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
+import org.graalvm.util.EconomicMap;
+
+import jdk.vm.ci.code.BailoutException;
+import jdk.vm.ci.code.CodeCacheProvider;
+import jdk.vm.ci.hotspot.EventProvider;
+import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
+import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
+import jdk.vm.ci.hotspot.HotSpotInstalledCode;
+import jdk.vm.ci.hotspot.HotSpotJVMCICompilerFactory;
+import jdk.vm.ci.hotspot.HotSpotJVMCIRuntimeProvider;
+import jdk.vm.ci.hotspot.HotSpotNmethod;
+import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
+import jdk.vm.ci.runtime.JVMCICompiler;
+import jdk.vm.ci.services.JVMCIServiceLocator;
+
+public class CompilationTask {
+
+    private static final EventProvider eventProvider;
+
+    static {
+        List<EventProvider> providers = JVMCIServiceLocator.getProviders(EventProvider.class);
+        if (providers.size() > 1) {
+            throw new GraalError("Multiple %s providers found: %s", EventProvider.class.getName(), providers);
+        } else if (providers.isEmpty()) {
+            eventProvider = EventProvider.createEmptyEventProvider();
+        } else {
+            eventProvider = providers.get(0);
+        }
+    }
+
+    private final HotSpotJVMCIRuntimeProvider jvmciRuntime;
+
+    private final HotSpotGraalCompiler compiler;
+    private final HotSpotCompilationIdentifier compilationId;
+
+    private HotSpotInstalledCode installedCode;
+
+    /**
+     * Specifies whether the compilation result is installed as the
+     * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method.
+     */
+    private final boolean installAsDefault;
+
+    private final boolean useProfilingInfo;
+    private final OptionValues options;
+
+    final class HotSpotCompilationWrapper extends CompilationWrapper<HotSpotCompilationRequestResult> {
+        private final EventProvider.CompilationEvent compilationEvent;
+        CompilationResult result;
+
+        HotSpotCompilationWrapper(EventProvider.CompilationEvent compilationEvent) {
+            super(compiler.getGraalRuntime().getOutputDirectory(), compiler.getGraalRuntime().getCompilationProblemsPerAction());
+            this.compilationEvent = compilationEvent;
+        }
+
+        @Override
+        protected DebugContext createRetryDebugContext(OptionValues retryOptions) {
+            SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
+            return DebugContext.create(retryOptions, new GraalDebugHandlersFactory(snippetReflection));
+        }
+
+        @Override
+        public String toString() {
+            return getMethod().format("%H.%n(%p)");
+        }
+
+        @Override
+        protected HotSpotCompilationRequestResult handleException(Throwable t) {
+            if (t instanceof BailoutException) {
+                BailoutException bailout = (BailoutException) t;
+                /*
+                 * Handling of permanent bailouts: Permanent bailouts that can happen for example
+                 * due to unsupported unstructured control flow in the bytecodes of a method must
+                 * not be retried. Hotspot compile broker will ensure that no recompilation at the
+                 * given tier will happen if retry is false.
+                 */
+                return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent());
+            }
+            // Log a failure event.
+            EventProvider.CompilerFailureEvent event = eventProvider.newCompilerFailureEvent();
+            if (event.shouldWrite()) {
+                event.setCompileId(getId());
+                event.setMessage(t.getMessage());
+                event.commit();
+            }
+
+            /*
+             * Treat random exceptions from the compiler as indicating a problem compiling this
+             * method. Report the result of toString instead of getMessage to ensure that the
+             * exception type is included in the output in case there's no detail mesage.
+             */
+            return HotSpotCompilationRequestResult.failure(t.toString(), false);
+        }
+
+        @Override
+        protected ExceptionAction lookupAction(OptionValues values, EnumOptionKey<ExceptionAction> actionKey) {
+            /*
+             * Automatically exit VM on non-bailout during bootstrap or when asserts are enabled but
+             * respect CompilationFailureAction if it has been explicitly set.
+             */
+            if (actionKey == CompilationFailureAction && !actionKey.hasBeenSet(values)) {
+                if (Assertions.assertionsEnabled() || compiler.getGraalRuntime().isBootstrapping()) {
+                    return ExitVM;
+                }
+            }
+            return super.lookupAction(values, actionKey);
+        }
+
+        @SuppressWarnings("try")
+        @Override
+        protected HotSpotCompilationRequestResult performCompilation(DebugContext debug) {
+            HotSpotResolvedJavaMethod method = getMethod();
+            int entryBCI = getEntryBCI();
+            final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
+            CompilationStatistics stats = CompilationStatistics.create(options, method, isOSR);
+
+            final CompilationPrinter printer = CompilationPrinter.begin(options, compilationId, method, entryBCI);
+
+            try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
+                // Begin the compilation event.
+                compilationEvent.begin();
+                result = compiler.compile(method, entryBCI, useProfilingInfo, compilationId, options, debug);
+            } catch (Throwable e) {
+                throw debug.handle(e);
+            } finally {
+                // End the compilation event.
+                compilationEvent.end();
+            }
+
+            if (result != null) {
+                try (DebugCloseable b = CodeInstallationTime.start(debug)) {
+                    installMethod(debug, result);
+                }
+                // Installation is included in compilation time and memory usage reported by printer
+                printer.finish(result);
+            }
+            stats.finish(method, installedCode);
+            if (result != null) {
+                return HotSpotCompilationRequestResult.success(result.getBytecodeSize() - method.getCodeSize());
+            }
+            return null;
+        }
+    }
+
+    public CompilationTask(HotSpotJVMCIRuntimeProvider jvmciRuntime, HotSpotGraalCompiler compiler, HotSpotCompilationRequest request, boolean useProfilingInfo, boolean installAsDefault,
+                    OptionValues options) {
+        this.jvmciRuntime = jvmciRuntime;
+        this.compiler = compiler;
+        this.compilationId = new HotSpotCompilationIdentifier(request);
+        this.useProfilingInfo = useProfilingInfo;
+        this.installAsDefault = installAsDefault;
+
+        /*
+         * Disable inlining if HotSpot has it disabled unless it's been explicitly set in Graal.
+         */
+        HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
+        GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
+        OptionValues newOptions = options;
+        if (!config.inline) {
+            EconomicMap<OptionKey<?>, Object> m = OptionValues.newOptionMap();
+            if (Inline.getValue(options) && !Inline.hasBeenSet(options)) {
+                m.put(Inline, false);
+            }
+            if (InlineDuringParsing.getValue(options) && !InlineDuringParsing.hasBeenSet(options)) {
+                m.put(InlineDuringParsing, false);
+            }
+            if (!m.isEmpty()) {
+                newOptions = new OptionValues(options, m);
+            }
+        }
+        this.options = newOptions;
+    }
+
+    public HotSpotResolvedJavaMethod getMethod() {
+        return getRequest().getMethod();
+    }
+
+    CompilationIdentifier getCompilationIdentifier() {
+        return compilationId;
+    }
+
+    /**
+     * Returns the HotSpot id of this compilation.
+     *
+     * @return HotSpot compile id
+     */
+    public int getId() {
+        return getRequest().getId();
+    }
+
+    public int getEntryBCI() {
+        return getRequest().getEntryBCI();
+    }
+
+    /**
+     * @return the compilation id plus a trailing '%' is the compilation is an OSR to match
+     *         PrintCompilation style output
+     */
+    public String getIdString() {
+        if (getEntryBCI() != JVMCICompiler.INVOCATION_ENTRY_BCI) {
+            return getId() + "%";
+        } else {
+            return Integer.toString(getId());
+        }
+    }
+
+    public HotSpotInstalledCode getInstalledCode() {
+        return installedCode;
+    }
+
+    /**
+     * Time spent in compilation.
+     */
+    private static final TimerKey CompilationTime = DebugContext.timer("CompilationTime").doc("Time spent in compilation and code installation.");
+
+    /**
+     * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes}.
+     */
+    private static final CounterKey CompiledBytecodes = DebugContext.counter("CompiledBytecodes");
+
+    /**
+     * Counts the number of compiled {@linkplain CompilationResult#getBytecodeSize() bytecodes} for
+     * which {@linkplain CompilationResult#getTargetCode()} code was installed.
+     */
+    private static final CounterKey CompiledAndInstalledBytecodes = DebugContext.counter("CompiledAndInstalledBytecodes");
+
+    /**
+     * Counts the number of installed {@linkplain CompilationResult#getTargetCodeSize()} bytes.
+     */
+    private static final CounterKey InstalledCodeSize = DebugContext.counter("InstalledCodeSize");
+
+    /**
+     * Time spent in code installation.
+     */
+    public static final TimerKey CodeInstallationTime = DebugContext.timer("CodeInstallation");
+
+    public HotSpotCompilationRequestResult runCompilation() {
+        SnippetReflectionProvider snippetReflection = compiler.getGraalRuntime().getHostProviders().getSnippetReflection();
+        try (DebugContext debug = DebugContext.create(options, new GraalDebugHandlersFactory(snippetReflection))) {
+            return runCompilation(debug);
+        }
+    }
+
+    @SuppressWarnings("try")
+    public HotSpotCompilationRequestResult runCompilation(DebugContext debug) {
+        HotSpotGraalRuntimeProvider graalRuntime = compiler.getGraalRuntime();
+        GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
+        int entryBCI = getEntryBCI();
+        boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
+        HotSpotResolvedJavaMethod method = getMethod();
+
+        // Log a compilation event.
+        EventProvider.CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
+
+        if (installAsDefault) {
+            // If there is already compiled code for this method on our level we simply return.
+            // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we
+            // only need to check for that value.
+            if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
+                return HotSpotCompilationRequestResult.failure("Already compiled", false);
+            }
+            if (HotSpotGraalCompilerFactory.checkGraalCompileOnlyFilter(method.getDeclaringClass().toJavaName(), method.getName(), method.getSignature().toString(),
+                            HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) != HotSpotJVMCICompilerFactory.CompilationLevel.FullOptimization) {
+                return HotSpotCompilationRequestResult.failure("GraalCompileOnly excluded", false);
+            }
+        }
+
+        HotSpotCompilationWrapper compilation = new HotSpotCompilationWrapper(compilationEvent);
+        try (DebugCloseable a = CompilationTime.start(debug)) {
+            return compilation.run(debug);
+        } finally {
+            try {
+                int compiledBytecodes = 0;
+                int codeSize = 0;
+
+                if (compilation.result != null) {
+                    compiledBytecodes = compilation.result.getBytecodeSize();
+                    CompiledBytecodes.add(debug, compiledBytecodes);
+                    if (installedCode != null) {
+                        codeSize = installedCode.getSize();
+                        CompiledAndInstalledBytecodes.add(debug, compiledBytecodes);
+                        InstalledCodeSize.add(debug, codeSize);
+                    }
+                }
+
+                // Log a compilation event.
+                if (compilationEvent.shouldWrite()) {
+                    compilationEvent.setMethod(method.format("%H.%n(%p)"));
+                    compilationEvent.setCompileId(getId());
+                    compilationEvent.setCompileLevel(config.compilationLevelFullOptimization);
+                    compilationEvent.setSucceeded(compilation.result != null && installedCode != null);
+                    compilationEvent.setIsOsr(isOSR);
+                    compilationEvent.setCodeSize(codeSize);
+                    compilationEvent.setInlinedBytes(compiledBytecodes);
+                    compilationEvent.commit();
+                }
+            } catch (Throwable t) {
+                return compilation.handleException(t);
+            }
+        }
+    }
+
+    @SuppressWarnings("try")
+    private void installMethod(DebugContext debug, final CompilationResult compResult) {
+        final CodeCacheProvider codeCache = jvmciRuntime.getHostJVMCIBackend().getCodeCache();
+        HotSpotBackend backend = compiler.getGraalRuntime().getHostBackend();
+        installedCode = null;
+        Object[] context = {new DebugDumpScope(getIdString(), true), codeCache, getMethod(), compResult};
+        try (DebugContext.Scope s = debug.scope("CodeInstall", context)) {
+            installedCode = (HotSpotInstalledCode) backend.createInstalledCode(debug, getRequest().getMethod(), getRequest(), compResult,
+                            getRequest().getMethod().getSpeculationLog(), null, installAsDefault, context);
+        } catch (Throwable e) {
+            throw debug.handle(e);
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Compilation[id=" + getId() + ", " + getMethod().format("%H.%n(%p)") + (getEntryBCI() == JVMCICompiler.INVOCATION_ENTRY_BCI ? "" : "@" + getEntryBCI()) + "]";
+    }
+
+    private HotSpotCompilationRequest getRequest() {
+        return compilationId.getRequest();
+    }
+}