001/*
002 * Copyright (c) 2013, 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.truffle.hotspot;
024
025import static com.oracle.graal.compiler.GraalCompiler.*;
026import static com.oracle.graal.graph.util.CollectionsAccess.*;
027import static com.oracle.graal.hotspot.meta.HotSpotSuitesProvider.*;
028import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
029import static jdk.internal.jvmci.code.CodeUtil.*;
030
031import java.util.*;
032import java.util.concurrent.*;
033import java.util.stream.*;
034
035import jdk.internal.jvmci.code.*;
036import jdk.internal.jvmci.code.CallingConvention.Type;
037import jdk.internal.jvmci.common.*;
038import jdk.internal.jvmci.hotspot.*;
039import jdk.internal.jvmci.meta.*;
040import jdk.internal.jvmci.service.*;
041
042import com.oracle.graal.api.runtime.*;
043import com.oracle.graal.compiler.*;
044import com.oracle.graal.compiler.target.*;
045import com.oracle.graal.debug.*;
046import com.oracle.graal.debug.Debug.Scope;
047import com.oracle.graal.graphbuilderconf.*;
048import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
049import com.oracle.graal.hotspot.*;
050import com.oracle.graal.hotspot.meta.*;
051import com.oracle.graal.java.*;
052import com.oracle.graal.lir.asm.*;
053import com.oracle.graal.lir.phases.*;
054import com.oracle.graal.nodes.*;
055import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
056import com.oracle.graal.phases.*;
057import com.oracle.graal.phases.common.inlining.*;
058import com.oracle.graal.phases.tiers.*;
059import com.oracle.graal.phases.util.*;
060import com.oracle.graal.runtime.*;
061import com.oracle.graal.truffle.*;
062import com.oracle.graal.truffle.hotspot.nfi.*;
063import com.oracle.nfi.api.*;
064import com.oracle.truffle.api.*;
065import com.oracle.truffle.api.nodes.*;
066
067/**
068 * Implementation of the Truffle runtime when running on top of Graal.
069 */
070public final class HotSpotTruffleRuntime extends GraalTruffleRuntime {
071
072    public static TruffleRuntime makeInstance() {
073        if (GraalTruffleRuntime.alternateRuntime != null) {
074            return GraalTruffleRuntime.alternateRuntime;
075        }
076        return new HotSpotTruffleRuntime();
077    }
078
079    private Map<OptimizedCallTarget, Future<?>> compilations = newIdentityMap();
080    private final ThreadPoolExecutor compileQueue;
081
082    private final Map<RootCallTarget, Void> callTargets = Collections.synchronizedMap(new WeakHashMap<RootCallTarget, Void>());
083    private static final long THREAD_EETOP_OFFSET = eetopOffset();
084
085    private HotSpotTruffleRuntime() {
086        installOptimizedCallTargetCallMethod();
087        lookupCallMethods(getGraalProviders().getMetaAccess());
088
089        installDefaultListeners();
090
091        // Create compilation queue.
092        CompilerThreadFactory factory = new CompilerThreadFactory("TruffleCompilerThread", new CompilerThreadFactory.DebugConfigAccess() {
093            public GraalDebugConfig getDebugConfig() {
094                if (Debug.isEnabled()) {
095                    GraalDebugConfig debugConfig = DebugEnvironment.initialize(TTY.out().out());
096                    debugConfig.dumpHandlers().add(new TruffleTreeDumpHandler());
097                    return debugConfig;
098                } else {
099                    return null;
100                }
101            }
102        });
103        int selectedProcessors = TruffleCompilerOptions.TruffleCompilerThreads.getValue();
104        if (selectedProcessors == 0) {
105            // No manual selection made, check how many processors are available.
106            int availableProcessors = Runtime.getRuntime().availableProcessors();
107            if (availableProcessors >= 4) {
108                selectedProcessors = 2;
109            } else if (availableProcessors >= 12) {
110                selectedProcessors = 4;
111            }
112        }
113        selectedProcessors = Math.max(1, selectedProcessors);
114        compileQueue = new ThreadPoolExecutor(selectedProcessors, selectedProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory);
115
116    }
117
118    @Override
119    public String getName() {
120        return "Graal Truffle Runtime";
121    }
122
123    @Override
124    public TruffleCompiler getTruffleCompiler() {
125        if (truffleCompiler == null) {
126            truffleCompiler = DefaultTruffleCompiler.create();
127        }
128        return truffleCompiler;
129    }
130
131    @Override
132    public RootCallTarget createCallTarget(RootNode rootNode) {
133        return createCallTargetImpl(null, rootNode);
134    }
135
136    private RootCallTarget createCallTargetImpl(OptimizedCallTarget source, RootNode rootNode) {
137        CompilationPolicy compilationPolicy;
138        if (acceptForCompilation(rootNode)) {
139            compilationPolicy = new CounterAndTimeBasedCompilationPolicy();
140        } else {
141            compilationPolicy = new InterpreterOnlyCompilationPolicy();
142        }
143        OptimizedCallTarget target = new OptimizedCallTarget(source, rootNode, this, compilationPolicy, new HotSpotSpeculationLog());
144        rootNode.setCallTarget(target);
145        callTargets.put(target, null);
146
147        return target;
148    }
149
150    @Override
151    public RootCallTarget createClonedCallTarget(OptimizedCallTarget source, RootNode root) {
152        return createCallTargetImpl(source, root);
153    }
154
155    public static void installOptimizedCallTargetCallMethod() {
156        Providers providers = getGraalProviders();
157        MetaAccessProvider metaAccess = providers.getMetaAccess();
158        ResolvedJavaType type = metaAccess.lookupJavaType(OptimizedCallTarget.class);
159        for (ResolvedJavaMethod method : type.getDeclaredMethods()) {
160            if (method.getAnnotation(TruffleCallBoundary.class) != null) {
161                CompilationResult compResult = compileMethod(method);
162                CodeCacheProvider codeCache = providers.getCodeCache();
163                try (Scope s = Debug.scope("CodeInstall", codeCache, method)) {
164                    codeCache.setDefaultMethod(method, compResult);
165                } catch (Throwable e) {
166                    throw Debug.handle(e);
167                }
168            }
169        }
170    }
171
172    private static CompilationResultBuilderFactory getOptimizedCallTargetInstrumentationFactory(String arch, ResolvedJavaMethod method) {
173        for (OptimizedCallTargetInstrumentationFactory factory : Services.load(OptimizedCallTargetInstrumentationFactory.class)) {
174            if (factory.getArchitecture().equals(arch)) {
175                factory.setInstrumentedMethod(method);
176                return factory;
177            }
178        }
179        // No specialization of OptimizedCallTarget on this platform.
180        return CompilationResultBuilderFactory.Default;
181    }
182
183    private static CompilationResult compileMethod(ResolvedJavaMethod javaMethod) {
184        HotSpotProviders providers = getGraalProviders();
185        SuitesProvider suitesProvider = providers.getSuites();
186        Suites suites = suitesProvider.createSuites();
187        LIRSuites lirSuites = suitesProvider.createLIRSuites();
188        removeInliningPhase(suites);
189        StructuredGraph graph = new StructuredGraph(javaMethod, AllowAssumptions.NO);
190
191        MetaAccessProvider metaAccess = providers.getMetaAccess();
192        Plugins plugins = new Plugins(new InvocationPlugins(metaAccess));
193        boolean infoPoints = HotSpotGraalRuntime.runtime().getCompilerToVM().shouldDebugNonSafepoints();
194        GraphBuilderConfiguration config = infoPoints ? GraphBuilderConfiguration.getInfopointEagerDefault(plugins) : GraphBuilderConfiguration.getEagerDefault(plugins);
195        new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.ALL, null).apply(graph);
196
197        PhaseSuite<HighTierContext> graphBuilderSuite = getGraphBuilderSuite(suitesProvider);
198        CallingConvention cc = getCallingConvention(providers.getCodeCache(), Type.JavaCallee, graph.method(), false);
199        Backend backend = Graal.getRequiredCapability(RuntimeProvider.class).getHostBackend();
200        CompilationResultBuilderFactory factory = getOptimizedCallTargetInstrumentationFactory(backend.getTarget().arch.getName(), javaMethod);
201        return compileGraph(graph, cc, javaMethod, providers, backend, providers.getCodeCache().getTarget(), graphBuilderSuite, OptimisticOptimizations.ALL, getProfilingInfo(graph), suites,
202                        lirSuites, new CompilationResult(), factory);
203    }
204
205    private static HotSpotProviders getGraalProviders() {
206        RuntimeProvider runtimeProvider = Graal.getRequiredCapability(RuntimeProvider.class);
207        return (HotSpotProviders) runtimeProvider.getHostBackend().getProviders();
208    }
209
210    private static PhaseSuite<HighTierContext> getGraphBuilderSuite(SuitesProvider suitesProvider) {
211        PhaseSuite<HighTierContext> graphBuilderSuite = suitesProvider.getDefaultGraphBuilderSuite();
212        return withSimpleDebugInfoIfRequested(graphBuilderSuite);
213    }
214
215    private static void removeInliningPhase(Suites suites) {
216        ListIterator<BasePhase<? super HighTierContext>> inliningPhase = suites.getHighTier().findPhase(InliningPhase.class);
217        if (inliningPhase != null) {
218            inliningPhase.remove();
219        }
220    }
221
222    @Override
223    public void compile(OptimizedCallTarget optimizedCallTarget, boolean mayBeAsynchronous) {
224        /* Ensure compiler is created. */
225        getTruffleCompiler();
226
227        Runnable r = new Runnable() {
228            @Override
229            public void run() {
230                doCompile(optimizedCallTarget);
231            }
232        };
233        Future<?> future = compileQueue.submit(r);
234        this.compilations.put(optimizedCallTarget, future);
235        getCompilationNotify().notifyCompilationQueued(optimizedCallTarget);
236
237        if (!mayBeAsynchronous) {
238            try {
239                future.get();
240            } catch (ExecutionException e) {
241                if (TruffleCompilationExceptionsAreThrown.getValue() && !(e.getCause() instanceof BailoutException && !((BailoutException) e.getCause()).isPermanent())) {
242                    throw new RuntimeException(e.getCause());
243                } else {
244                    // silently ignored
245                }
246            } catch (InterruptedException | CancellationException e) {
247                // silently ignored
248            }
249        }
250    }
251
252    @Override
253    public boolean cancelInstalledTask(OptimizedCallTarget optimizedCallTarget, Object source, CharSequence reason) {
254        Future<?> codeTask = this.compilations.get(optimizedCallTarget);
255        if (codeTask != null && isCompiling(optimizedCallTarget)) {
256            this.compilations.remove(optimizedCallTarget);
257            boolean result = codeTask.cancel(true);
258            if (result) {
259                optimizedCallTarget.notifyCompilationFinished(false);
260                getCompilationNotify().notifyCompilationDequeued(optimizedCallTarget, source, reason);
261            }
262            return result;
263        }
264        return false;
265    }
266
267    @Override
268    public void waitForCompilation(OptimizedCallTarget optimizedCallTarget, long timeout) throws ExecutionException, TimeoutException {
269        Future<?> codeTask = this.compilations.get(optimizedCallTarget);
270        if (codeTask != null && isCompiling(optimizedCallTarget)) {
271            try {
272                codeTask.get(timeout, TimeUnit.MILLISECONDS);
273            } catch (InterruptedException e) {
274                // ignore interrupted
275            }
276        }
277    }
278
279    @Override
280    public Collection<OptimizedCallTarget> getQueuedCallTargets() {
281        return compilations.keySet().stream().filter(e -> !compilations.get(e).isDone()).collect(Collectors.toList());
282    }
283
284    @Override
285    public boolean isCompiling(OptimizedCallTarget optimizedCallTarget) {
286        Future<?> codeTask = this.compilations.get(optimizedCallTarget);
287        if (codeTask != null) {
288            if (codeTask.isCancelled() || codeTask.isDone()) {
289                this.compilations.remove(optimizedCallTarget);
290                return false;
291            }
292            return true;
293        }
294        return false;
295    }
296
297    @Override
298    public void invalidateInstalledCode(OptimizedCallTarget optimizedCallTarget, Object source, CharSequence reason) {
299        HotSpotGraalRuntime.runtime().getCompilerToVM().invalidateInstalledCode(optimizedCallTarget);
300        getCompilationNotify().notifyCompilationInvalidated(optimizedCallTarget, source, reason);
301    }
302
303    @Override
304    public void reinstallStubs() {
305        installOptimizedCallTargetCallMethod();
306    }
307
308    @Override
309    public boolean platformEnableInfopoints() {
310        return HotSpotGraalRuntime.runtime().getCompilerToVM().shouldDebugNonSafepoints();
311    }
312
313    @Override
314    public Collection<RootCallTarget> getCallTargets() {
315        return Collections.unmodifiableSet(callTargets.keySet());
316    }
317
318    @Override
319    public void notifyTransferToInterpreter() {
320        CompilerAsserts.neverPartOfCompilation();
321        if (TraceTruffleTransferToInterpreter.getValue()) {
322            long thread = UnsafeAccess.unsafe.getLong(Thread.currentThread(), THREAD_EETOP_OFFSET);
323            long pendingTransferToInterpreterAddress = thread + HotSpotGraalRuntime.runtime().getConfig().pendingTransferToInterpreterOffset;
324            boolean deoptimized = UnsafeAccess.unsafe.getByte(pendingTransferToInterpreterAddress) != 0;
325            if (deoptimized) {
326                UnsafeAccess.unsafe.putByte(pendingTransferToInterpreterAddress, (byte) 0);
327
328                logTransferToInterpreter();
329            }
330        }
331    }
332
333    private static void logTransferToInterpreter() {
334        final int skip = 2;
335        final int limit = TraceTruffleStackTraceLimit.getValue();
336        StackTraceElement[] stackTrace = new Throwable().getStackTrace();
337        String suffix = stackTrace.length > skip + limit ? "\n  ..." : "";
338        TTY.out().out().println(Arrays.stream(stackTrace).skip(skip).limit(limit).map(StackTraceElement::toString).collect(Collectors.joining("\n  ", "", suffix)));
339    }
340
341    private static long eetopOffset() {
342        try {
343            return UnsafeAccess.unsafe.objectFieldOffset(Thread.class.getDeclaredField("eetop"));
344        } catch (Exception e) {
345            throw new JVMCIError(e);
346        }
347    }
348
349    private static RawNativeCallNodeFactory getRawNativeCallNodeFactory(String arch) {
350        for (RawNativeCallNodeFactory factory : Services.load(RawNativeCallNodeFactory.class)) {
351            if (factory.getArchitecture().equals(arch)) {
352                return factory;
353            }
354        }
355        // No RawNativeCallNodeFactory on this platform.
356        return null;
357    }
358
359    public static NativeFunctionInterface createNativeFunctionInterface() {
360        HotSpotVMConfig config = HotSpotGraalRuntime.runtime().getConfig();
361        Backend backend = HotSpotGraalRuntime.runtime().getHostBackend();
362        RawNativeCallNodeFactory factory = getRawNativeCallNodeFactory(backend.getTarget().arch.getName());
363        if (factory == null) {
364            return null;
365        }
366        return new HotSpotNativeFunctionInterface(HotSpotGraalRuntime.runtime().getHostProviders(), factory, backend, config.dllLoad, config.dllLookup, config.rtldDefault);
367    }
368}