1 /*
2  * Copyright (c) 2015, 2020, 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  */
23 
24 
25 package org.graalvm.compiler.hotspot;
26 
27 import static org.graalvm.compiler.core.common.GraalOptions.OptAssumptions;
28 
29 import java.io.ByteArrayOutputStream;
30 import java.io.PrintStream;
31 import java.util.Collections;
32 import java.util.Formattable;
33 import java.util.Formatter;
34 import java.util.List;
35 
36 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler;
37 import org.graalvm.compiler.code.CompilationResult;
38 import org.graalvm.compiler.core.GraalCompiler;
39 import org.graalvm.compiler.core.common.CompilationIdentifier;
40 import org.graalvm.compiler.core.common.util.CompilationAlarm;
41 import org.graalvm.compiler.debug.DebugContext;
42 import org.graalvm.compiler.debug.DebugContext.Activation;
43 import org.graalvm.compiler.debug.DebugHandlersFactory;
44 import org.graalvm.compiler.debug.DebugOptions;
45 import org.graalvm.compiler.hotspot.CompilationCounters.Options;
46 import org.graalvm.compiler.hotspot.HotSpotGraalRuntime.HotSpotGC;
47 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
48 import org.graalvm.compiler.hotspot.phases.OnStackReplacementPhase;
49 import org.graalvm.compiler.java.GraphBuilderPhase;
50 import org.graalvm.compiler.lir.asm.CompilationResultBuilderFactory;
51 import org.graalvm.compiler.lir.phases.LIRSuites;
52 import org.graalvm.compiler.nodes.Cancellable;
53 import org.graalvm.compiler.nodes.StructuredGraph;
54 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions;
55 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
56 import org.graalvm.compiler.options.OptionValues;
57 import org.graalvm.compiler.phases.OptimisticOptimizations;
58 import org.graalvm.compiler.phases.OptimisticOptimizations.Optimization;
59 import org.graalvm.compiler.phases.PhaseSuite;
60 import org.graalvm.compiler.phases.tiers.HighTierContext;
61 import org.graalvm.compiler.phases.tiers.Suites;
62 import org.graalvm.compiler.printer.GraalDebugHandlersFactory;
63 import org.graalvm.compiler.serviceprovider.GraalUnsafeAccess;
64 
65 import jdk.vm.ci.code.CompilationRequest;
66 import jdk.vm.ci.code.CompilationRequestResult;
67 import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
68 import jdk.vm.ci.hotspot.HotSpotCompilationRequestResult;
69 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
70 import jdk.vm.ci.meta.DefaultProfilingInfo;
71 import jdk.vm.ci.meta.JavaMethod;
72 import jdk.vm.ci.meta.ProfilingInfo;
73 import jdk.vm.ci.meta.ResolvedJavaMethod;
74 import jdk.vm.ci.meta.SpeculationLog;
75 import jdk.vm.ci.meta.TriState;
76 import jdk.vm.ci.runtime.JVMCICompiler;
77 import sun.misc.Unsafe;
78 
79 public class HotSpotGraalCompiler implements GraalJVMCICompiler, Cancellable {
80 
81     private static final Unsafe UNSAFE = GraalUnsafeAccess.getUnsafe();
82     private final HotSpotJVMCIRuntime jvmciRuntime;
83     private final HotSpotGraalRuntimeProvider graalRuntime;
84     private final CompilationCounters compilationCounters;
85     private final BootstrapWatchDog bootstrapWatchDog;
86     private List<DebugHandlersFactory> factories;
87 
HotSpotGraalCompiler(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalRuntimeProvider graalRuntime, OptionValues options)88     HotSpotGraalCompiler(HotSpotJVMCIRuntime jvmciRuntime, HotSpotGraalRuntimeProvider graalRuntime, OptionValues options) {
89         this.jvmciRuntime = jvmciRuntime;
90         this.graalRuntime = graalRuntime;
91         // It is sufficient to have one compilation counter object per compiler object.
92         this.compilationCounters = Options.CompilationCountLimit.getValue(options) > 0 ? new CompilationCounters(options) : null;
93         this.bootstrapWatchDog = graalRuntime.isBootstrapping() && !DebugOptions.BootstrapInitializeOnly.getValue(options) ? BootstrapWatchDog.maybeCreate(graalRuntime) : null;
94     }
95 
getDebugHandlersFactories()96     public List<DebugHandlersFactory> getDebugHandlersFactories() {
97         if (factories == null) {
98             factories = Collections.singletonList(new GraalDebugHandlersFactory(graalRuntime.getHostProviders().getSnippetReflection()));
99         }
100         return factories;
101     }
102 
103     @Override
getGraalRuntime()104     public HotSpotGraalRuntimeProvider getGraalRuntime() {
105         return graalRuntime;
106     }
107 
108     @Override
compileMethod(CompilationRequest request)109     public CompilationRequestResult compileMethod(CompilationRequest request) {
110         return compileMethod(request, true, graalRuntime.getOptions());
111     }
112 
113     @SuppressWarnings("try")
compileMethod(CompilationRequest request, boolean installAsDefault, OptionValues initialOptions)114     CompilationRequestResult compileMethod(CompilationRequest request, boolean installAsDefault, OptionValues initialOptions) {
115         try (CompilationContext scope = HotSpotGraalServices.openLocalCompilationContext(request)) {
116             if (graalRuntime.isShutdown()) {
117                 return HotSpotCompilationRequestResult.failure(String.format("Shutdown entered"), true);
118             }
119 
120             ResolvedJavaMethod method = request.getMethod();
121 
122             if (graalRuntime.isBootstrapping()) {
123                 if (DebugOptions.BootstrapInitializeOnly.getValue(initialOptions)) {
124                     return HotSpotCompilationRequestResult.failure(String.format("Skip compilation because %s is enabled", DebugOptions.BootstrapInitializeOnly.getName()), true);
125                 }
126                 if (bootstrapWatchDog != null) {
127                     if (bootstrapWatchDog.hitCriticalCompilationRateOrTimeout()) {
128                         // Drain the compilation queue to expedite completion of the bootstrap
129                         return HotSpotCompilationRequestResult.failure("hit critical bootstrap compilation rate or timeout", true);
130                     }
131                 }
132             }
133             HotSpotCompilationRequest hsRequest = (HotSpotCompilationRequest) request;
134             CompilationTask task = new CompilationTask(jvmciRuntime, this, hsRequest, true, shouldRetainLocalVariables(hsRequest.getJvmciEnv()), installAsDefault);
135             OptionValues options = task.filterOptions(initialOptions);
136             try (CompilationWatchDog w1 = CompilationWatchDog.watch(method, hsRequest.getId(), options);
137                             BootstrapWatchDog.Watch w2 = bootstrapWatchDog == null ? null : bootstrapWatchDog.watch(request);
138                             CompilationAlarm alarm = CompilationAlarm.trackCompilationPeriod(options);) {
139                 if (compilationCounters != null) {
140                     compilationCounters.countCompilation(method);
141                 }
142                 CompilationRequestResult r = null;
143                 try (DebugContext debug = graalRuntime.openDebugContext(options, task.getCompilationIdentifier(), method, getDebugHandlersFactories(), DebugContext.getDefaultLogStream());
144                                 Activation a = debug.activate()) {
145                     r = task.runCompilation(debug);
146                 }
147                 assert r != null;
148                 return r;
149             }
150         }
151     }
152 
shouldRetainLocalVariables(long envAddress)153     private boolean shouldRetainLocalVariables(long envAddress) {
154         GraalHotSpotVMConfig config = graalRuntime.getVMConfig();
155         if (envAddress == 0) {
156             return false;
157         }
158         if (config.jvmciCompileStateCanPopFrameOffset != Integer.MIN_VALUE) {
159             if ((UNSAFE.getByte(envAddress + config.jvmciCompileStateCanPopFrameOffset) & 0xFF) != 0) {
160                 return true;
161             }
162         }
163         if (config.jvmciCompileStateCanAccessLocalVariablesOffset != Integer.MIN_VALUE) {
164             if ((UNSAFE.getByte(envAddress + config.jvmciCompileStateCanAccessLocalVariablesOffset) & 0xFF) != 0) {
165                 return true;
166             }
167         }
168         return false;
169     }
170 
171     @Override
isCancelled()172     public boolean isCancelled() {
173         return graalRuntime.isShutdown();
174     }
175 
createGraph(ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, CompilationIdentifier compilationId, OptionValues options, DebugContext debug)176     public StructuredGraph createGraph(ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, CompilationIdentifier compilationId, OptionValues options, DebugContext debug) {
177         HotSpotBackend backend = graalRuntime.getHostBackend();
178         HotSpotProviders providers = backend.getProviders();
179         final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
180         AllowAssumptions allowAssumptions = AllowAssumptions.ifTrue(OptAssumptions.getValue(options));
181         StructuredGraph graph = method.isNative() || isOSR ? null : providers.getReplacements().getIntrinsicGraph(method, compilationId, debug, allowAssumptions, this);
182 
183         if (graph == null) {
184             SpeculationLog speculationLog = method.getSpeculationLog();
185             if (speculationLog != null) {
186                 speculationLog.collectFailedSpeculations();
187             }
188             // @formatter:off
189             graph = new StructuredGraph.Builder(options, debug, allowAssumptions).
190                             method(method).
191                             cancellable(this).
192                             entryBCI(entryBCI).
193                             speculationLog(speculationLog).
194                             useProfilingInfo(useProfilingInfo).
195                             compilationId(compilationId).build();
196             // @formatter:on
197         }
198         return graph;
199     }
200 
compileHelper(CompilationResultBuilderFactory crbf, CompilationResult result, StructuredGraph graph, ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, OptionValues options)201     public CompilationResult compileHelper(CompilationResultBuilderFactory crbf, CompilationResult result, StructuredGraph graph, ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo,
202                     OptionValues options) {
203         return compileHelper(crbf, result, graph, method, entryBCI, useProfilingInfo, false, options);
204     }
205 
compileHelper(CompilationResultBuilderFactory crbf, CompilationResult result, StructuredGraph graph, ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, boolean shouldRetainLocalVariables, OptionValues options)206     public CompilationResult compileHelper(CompilationResultBuilderFactory crbf, CompilationResult result, StructuredGraph graph, ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo,
207                     boolean shouldRetainLocalVariables, OptionValues options) {
208         assert options == graph.getOptions();
209         HotSpotBackend backend = graalRuntime.getHostBackend();
210         HotSpotProviders providers = backend.getProviders();
211         final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
212 
213         Suites suites = getSuites(providers, options);
214         LIRSuites lirSuites = getLIRSuites(providers, options);
215         ProfilingInfo profilingInfo = useProfilingInfo ? method.getProfilingInfo(!isOSR, isOSR) : DefaultProfilingInfo.get(TriState.FALSE);
216         OptimisticOptimizations optimisticOpts = getOptimisticOpts(profilingInfo, options);
217 
218         /*
219          * Cut off never executed code profiles if there is code, e.g. after the osr loop, that is
220          * never executed.
221          */
222         if (isOSR && !OnStackReplacementPhase.Options.DeoptAfterOSR.getValue(options)) {
223             optimisticOpts.remove(Optimization.RemoveNeverExecutedCode);
224         }
225 
226         result.setEntryBCI(entryBCI);
227         boolean shouldDebugNonSafepoints = providers.getCodeCache().shouldDebugNonSafepoints();
228         PhaseSuite<HighTierContext> graphBuilderSuite = configGraphBuilderSuite(providers.getSuites().getDefaultGraphBuilderSuite(), shouldDebugNonSafepoints, shouldRetainLocalVariables, isOSR);
229         GraalCompiler.compileGraph(graph, method, providers, backend, graphBuilderSuite, optimisticOpts, profilingInfo, suites, lirSuites, result, crbf, true);
230 
231         if (!isOSR && useProfilingInfo) {
232             ProfilingInfo profile = profilingInfo;
233             profile.setCompilerIRSize(StructuredGraph.class, graph.getNodeCount());
234         }
235 
236         return result;
237     }
238 
compile(StructuredGraph graph, ResolvedJavaMethod method, int entryBCI, boolean useProfilingInfo, boolean shouldRetainLocalVariables, CompilationIdentifier compilationId, DebugContext debug)239     public CompilationResult compile(StructuredGraph graph,
240                     ResolvedJavaMethod method,
241                     int entryBCI,
242                     boolean useProfilingInfo,
243                     boolean shouldRetainLocalVariables,
244                     CompilationIdentifier compilationId,
245                     DebugContext debug) {
246         CompilationResult result = new CompilationResult(compilationId);
247         return compileHelper(CompilationResultBuilderFactory.Default, result, graph, method, entryBCI, useProfilingInfo, shouldRetainLocalVariables, debug.getOptions());
248     }
249 
getOptimisticOpts(ProfilingInfo profilingInfo, OptionValues options)250     protected OptimisticOptimizations getOptimisticOpts(ProfilingInfo profilingInfo, OptionValues options) {
251         return new OptimisticOptimizations(profilingInfo, options);
252     }
253 
getSuites(HotSpotProviders providers, OptionValues options)254     protected Suites getSuites(HotSpotProviders providers, OptionValues options) {
255         return providers.getSuites().getDefaultSuites(options);
256     }
257 
getLIRSuites(HotSpotProviders providers, OptionValues options)258     protected LIRSuites getLIRSuites(HotSpotProviders providers, OptionValues options) {
259         return providers.getSuites().getDefaultLIRSuites(options);
260     }
261 
262     /**
263      * Reconfigures a given graph builder suite (GBS) if one of the given GBS parameter values is
264      * not the default.
265      *
266      * @param suite the graph builder suite
267      * @param shouldDebugNonSafepoints specifies if extra debug info should be generated (default is
268      *            false)
269      * @param shouldRetainLocalVariables specifies if local variables should be retained for
270      *            debugging purposes (default is false)
271      * @param isOSR specifies if extra OSR-specific post-processing is required (default is false)
272      * @return a new suite derived from {@code suite} if any of the GBS parameters did not have a
273      *         default value otherwise {@code suite}
274      */
configGraphBuilderSuite(PhaseSuite<HighTierContext> suite, boolean shouldDebugNonSafepoints, boolean shouldRetainLocalVariables, boolean isOSR)275     protected PhaseSuite<HighTierContext> configGraphBuilderSuite(PhaseSuite<HighTierContext> suite, boolean shouldDebugNonSafepoints, boolean shouldRetainLocalVariables, boolean isOSR) {
276         if (shouldDebugNonSafepoints || shouldRetainLocalVariables || isOSR) {
277             PhaseSuite<HighTierContext> newGbs = suite.copy();
278             GraphBuilderPhase graphBuilderPhase = (GraphBuilderPhase) newGbs.findPhase(GraphBuilderPhase.class).previous();
279             GraphBuilderConfiguration graphBuilderConfig = graphBuilderPhase.getGraphBuilderConfig();
280             if (shouldDebugNonSafepoints) {
281                 graphBuilderConfig = graphBuilderConfig.withNodeSourcePosition(true);
282             }
283             if (shouldRetainLocalVariables) {
284                 graphBuilderConfig = graphBuilderConfig.withRetainLocalVariables(true);
285             }
286             GraphBuilderPhase newGraphBuilderPhase = new GraphBuilderPhase(graphBuilderConfig);
287             newGbs.findPhase(GraphBuilderPhase.class).set(newGraphBuilderPhase);
288             if (isOSR) {
289                 newGbs.appendPhase(new OnStackReplacementPhase());
290             }
291             return newGbs;
292         }
293         return suite;
294     }
295 
296     /**
297      * Converts {@code method} to a String with {@link JavaMethod#format(String)} and the format
298      * string {@code "%H.%n(%p)"}.
299      */
str(JavaMethod method)300     static String str(JavaMethod method) {
301         return method.format("%H.%n(%p)");
302     }
303 
304     /**
305      * Wraps {@code obj} in a {@link Formatter} that standardizes formatting for certain objects.
306      */
fmt(Object obj)307     static Formattable fmt(Object obj) {
308         return new Formattable() {
309             @Override
310             public void formatTo(Formatter buf, int flags, int width, int precision) {
311                 if (obj instanceof Throwable) {
312                     ByteArrayOutputStream baos = new ByteArrayOutputStream();
313                     ((Throwable) obj).printStackTrace(new PrintStream(baos));
314                     buf.format("%s", baos.toString());
315                 } else if (obj instanceof StackTraceElement[]) {
316                     for (StackTraceElement e : (StackTraceElement[]) obj) {
317                         buf.format("\t%s%n", e);
318                     }
319                 } else if (obj instanceof JavaMethod) {
320                     buf.format("%s", str((JavaMethod) obj));
321                 } else {
322                     buf.format("%s", obj);
323                 }
324             }
325         };
326     }
327 
328     @Override
329     public boolean isGCSupported(int gcIdentifier) {
330         HotSpotGC gc = HotSpotGC.forName(gcIdentifier, graalRuntime.getVMConfig());
331         if (gc != null) {
332             return gc.supported;
333         }
334         return false;
335     }
336 }
337