001/*
002 * Copyright (c) 2012, 2015, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.hotspot;
024
025import static jdk.internal.jvmci.common.UnsafeAccess.*;
026import static jdk.internal.jvmci.compiler.Compiler.*;
027
028import java.util.concurrent.*;
029
030import com.oracle.graal.debug.*;
031import com.oracle.graal.debug.Debug.Scope;
032import com.oracle.graal.debug.internal.*;
033
034import jdk.internal.jvmci.code.*;
035import jdk.internal.jvmci.compiler.Compiler;
036import jdk.internal.jvmci.hotspot.*;
037import jdk.internal.jvmci.hotspot.events.*;
038import jdk.internal.jvmci.hotspot.events.EventProvider.CompilationEvent;
039import jdk.internal.jvmci.hotspot.events.EventProvider.CompilerFailureEvent;
040import jdk.internal.jvmci.meta.*;
041import jdk.internal.jvmci.service.*;
042
043//JaCoCo Exclude
044
045public class CompilationTask {
046
047    private static final DebugMetric BAILOUTS = Debug.metric("Bailouts");
048
049    private static final EventProvider eventProvider;
050    static {
051        EventProvider provider = Services.loadSingle(EventProvider.class, false);
052        if (provider == null) {
053            eventProvider = new EmptyEventProvider();
054        } else {
055            eventProvider = provider;
056        }
057    }
058    private static final Compiler compiler = Services.loadSingle(Compiler.class, true);
059
060    private final HotSpotResolvedJavaMethod method;
061    private final int entryBCI;
062    private final int id;
063
064    /**
065     * Specifies whether the compilation result is installed as the
066     * {@linkplain HotSpotNmethod#isDefault() default} nmethod for the compiled method.
067     */
068    private final boolean installAsDefault;
069
070    static class Lazy {
071        /**
072         * A {@link com.sun.management.ThreadMXBean} to be able to query some information about the
073         * current compiler thread, e.g. total allocated bytes.
074         */
075        static final com.sun.management.ThreadMXBean threadMXBean = (com.sun.management.ThreadMXBean) Management.getThreadMXBean();
076    }
077
078    /**
079     * The address of the JVMCIEnv associated with this compilation or 0L if no such object exists.
080     */
081    private final long jvmciEnv;
082
083    public CompilationTask(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id, boolean installAsDefault) {
084        this.method = method;
085        this.entryBCI = entryBCI;
086        this.id = id;
087        this.jvmciEnv = jvmciEnv;
088        this.installAsDefault = installAsDefault;
089    }
090
091    public ResolvedJavaMethod getMethod() {
092        return method;
093    }
094
095    /**
096     * Returns the compilation id of this task.
097     *
098     * @return compile id
099     */
100    public int getId() {
101        return id;
102    }
103
104    public int getEntryBCI() {
105        return entryBCI;
106    }
107
108    /**
109     * Time spent in compilation.
110     */
111    private static final DebugTimer CompilationTime = Debug.timer("CompilationTime");
112
113    /**
114     * Meters the {@linkplain CompilationResult#getBytecodeSize() bytecodes} compiled.
115     */
116    private static final DebugMetric CompiledBytecodes = Debug.metric("CompiledBytecodes");
117
118    public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation");
119
120    public void runCompilation() {
121        HotSpotVMConfig config = HotSpotJVMCIRuntime.runtime().getConfig();
122        final long threadId = Thread.currentThread().getId();
123        long startCompilationTime = System.nanoTime();
124        HotSpotInstalledCode installedCode = null;
125        final boolean isOSR = entryBCI != Compiler.INVOCATION_ENTRY_BCI;
126
127        // Log a compilation event.
128        CompilationEvent compilationEvent = eventProvider.newCompilationEvent();
129
130        // If there is already compiled code for this method on our level we simply return.
131        // JVMCI compiles are always at the highest compile level, even in non-tiered mode so we
132        // only need to check for that value.
133        if (method.hasCodeAtLevel(entryBCI, config.compilationLevelFullOptimization)) {
134            return;
135        }
136
137        CompilationResult result = null;
138        try (DebugCloseable a = CompilationTime.start()) {
139            CompilationStatistics stats = CompilationStatistics.create(method, isOSR);
140            final boolean printCompilation = PrintCompilation.getValue() && !TTY.isSuppressed();
141            final boolean printAfterCompilation = PrintAfterCompilation.getValue() && !TTY.isSuppressed();
142            if (printCompilation) {
143                TTY.println(getMethodDescription() + "...");
144            }
145
146            TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), method);
147            final long start;
148            final long allocatedBytesBefore;
149            if (printAfterCompilation || printCompilation) {
150                start = System.currentTimeMillis();
151                allocatedBytesBefore = printAfterCompilation || printCompilation ? Lazy.threadMXBean.getThreadAllocatedBytes(threadId) : 0L;
152            } else {
153                start = 0L;
154                allocatedBytesBefore = 0L;
155            }
156
157            try (Scope s = Debug.scope("Compiling", new DebugDumpScope(String.valueOf(id), true))) {
158                // Begin the compilation event.
159                compilationEvent.begin();
160
161                result = compiler.compile(method, entryBCI, mustRecordMethodInlining(config));
162
163                result.setId(getId());
164            } catch (Throwable e) {
165                throw Debug.handle(e);
166            } finally {
167                // End the compilation event.
168                compilationEvent.end();
169
170                filter.remove();
171
172                if (printAfterCompilation || printCompilation) {
173                    final long stop = System.currentTimeMillis();
174                    final int targetCodeSize = result != null ? result.getTargetCodeSize() : -1;
175                    final long allocatedBytesAfter = Lazy.threadMXBean.getThreadAllocatedBytes(threadId);
176                    final long allocatedBytes = (allocatedBytesAfter - allocatedBytesBefore) / 1024;
177
178                    if (printAfterCompilation) {
179                        TTY.println(getMethodDescription() + String.format(" | %4dms %5dB %5dkB", stop - start, targetCodeSize, allocatedBytes));
180                    } else if (printCompilation) {
181                        TTY.println(String.format("%-6d JVMCI %-70s %-45s %-50s | %4dms %5dB %5dkB", id, "", "", "", stop - start, targetCodeSize, allocatedBytes));
182                    }
183                }
184            }
185
186            try (DebugCloseable b = CodeInstallationTime.start()) {
187                installedCode = (HotSpotInstalledCode) installMethod(result);
188            }
189            stats.finish(method, installedCode);
190        } catch (BailoutException bailout) {
191            BAILOUTS.increment();
192            if (ExitVMOnBailout.getValue()) {
193                TTY.out.println(method.format("Bailout in %H.%n(%p)"));
194                bailout.printStackTrace(TTY.out);
195                System.exit(-1);
196            } else if (PrintBailout.getValue()) {
197                TTY.out.println(method.format("Bailout in %H.%n(%p)"));
198                bailout.printStackTrace(TTY.out);
199            }
200        } catch (Throwable t) {
201            if (PrintStackTraceOnException.getValue() || ExitVMOnException.getValue()) {
202                t.printStackTrace(TTY.out);
203            }
204
205            // Log a failure event.
206            CompilerFailureEvent event = eventProvider.newCompilerFailureEvent();
207            if (event.shouldWrite()) {
208                event.setCompileId(getId());
209                event.setMessage(t.getMessage());
210                event.commit();
211            }
212
213            if (ExitVMOnException.getValue()) {
214                System.exit(-1);
215            }
216        } finally {
217            int compiledBytecodes = 0;
218            int codeSize = 0;
219            if (result != null) {
220                compiledBytecodes = result.getBytecodeSize();
221            }
222            if (installedCode != null) {
223                codeSize = installedCode.getSize();
224            }
225            CompiledBytecodes.add(compiledBytecodes);
226
227            // Log a compilation event.
228            if (compilationEvent.shouldWrite()) {
229                compilationEvent.setMethod(method.format("%H.%n(%p)"));
230                compilationEvent.setCompileId(getId());
231                compilationEvent.setCompileLevel(config.compilationLevelFullOptimization);
232                compilationEvent.setSucceeded(result != null && installedCode != null);
233                compilationEvent.setIsOsr(isOSR);
234                compilationEvent.setCodeSize(codeSize);
235                compilationEvent.setInlinedBytes(compiledBytecodes);
236                compilationEvent.commit();
237            }
238
239            if (jvmciEnv != 0) {
240                long ctask = unsafe.getAddress(jvmciEnv + config.jvmciEnvTaskOffset);
241                assert ctask != 0L;
242                unsafe.putInt(ctask + config.compileTaskNumInlinedBytecodesOffset, compiledBytecodes);
243            }
244            long compilationTime = System.nanoTime() - startCompilationTime;
245            if ((config.ciTime || config.ciTimeEach) && installedCode != null) {
246                long timeUnitsPerSecond = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
247                CompilerToVM c2vm = HotSpotJVMCIRuntime.runtime().getCompilerToVM();
248                c2vm.notifyCompilationStatistics(id, method, entryBCI != Compiler.INVOCATION_ENTRY_BCI, compiledBytecodes, compilationTime, timeUnitsPerSecond, installedCode);
249            }
250        }
251    }
252
253    /**
254     * Determines whether to disable method inlining recording for the method being compiled.
255     */
256    private boolean mustRecordMethodInlining(HotSpotVMConfig config) {
257        if (config.ciTime || config.ciTimeEach || CompiledBytecodes.isEnabled()) {
258            return true;
259        }
260        if (jvmciEnv == 0 || unsafe.getByte(jvmciEnv + config.jvmciEnvJvmtiCanHotswapOrPostBreakpointOffset) != 0) {
261            return true;
262        }
263        return false;
264    }
265
266    private String getMethodDescription() {
267        return String.format("%-6d JVMCI %-70s %-45s %-50s %s", id, method.getDeclaringClass().getName(), method.getName(), method.getSignature().toMethodDescriptor(),
268                        entryBCI == Compiler.INVOCATION_ENTRY_BCI ? "" : "(OSR@" + entryBCI + ") ");
269    }
270
271    private InstalledCode installMethod(final CompilationResult compResult) {
272        final HotSpotCodeCacheProvider codeCache = (HotSpotCodeCacheProvider) HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getCodeCache();
273        InstalledCode installedCode = null;
274        try (Scope s = Debug.scope("CodeInstall", new DebugDumpScope(String.valueOf(id), true), codeCache, method)) {
275            installedCode = codeCache.installMethod(method, compResult, jvmciEnv, installAsDefault);
276        } catch (Throwable e) {
277            throw Debug.handle(e);
278        }
279        return installedCode;
280    }
281
282    @Override
283    public String toString() {
284        return "Compilation[id=" + id + ", " + method.format("%H.%n(%p)") + (entryBCI == Compiler.INVOCATION_ENTRY_BCI ? "" : "@" + entryBCI) + "]";
285    }
286
287    /**
288     * Compiles a method to machine code.
289     */
290    public static void compileMethod(HotSpotResolvedJavaMethod method, int entryBCI, long jvmciEnv, int id) {
291        // Ensure a debug configuration for this thread is initialized
292        if (Debug.isEnabled() && DebugScope.getConfig() == null) {
293            DebugEnvironment.initialize(TTY.out);
294        }
295
296        CompilationTask task = new CompilationTask(method, entryBCI, jvmciEnv, id, true);
297        try (DebugConfigScope dcs = Debug.setConfig(new TopLevelDebugConfig())) {
298            task.runCompilation();
299        }
300        return;
301    }
302}