Skip to content

Commit d184245

Browse files
tkrodriguezjchalou
authored andcommitted
Automatically detect deopt recompile cycles
1 parent c460f60 commit d184245

File tree

12 files changed

+519
-25
lines changed

12 files changed

+519
-25
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/core/test/VerifyProfileMethodUsage.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import java.lang.reflect.Method;
2828

2929
import org.graalvm.collections.EconomicSet;
30+
3031
import jdk.graal.compiler.debug.GraalError;
3132
import jdk.graal.compiler.nodes.StructuredGraph;
3233
import jdk.graal.compiler.nodes.java.MethodCallTargetNode;
@@ -35,7 +36,7 @@
3536
import jdk.graal.compiler.nodes.spi.ResolvedJavaMethodProfileProvider;
3637
import jdk.graal.compiler.nodes.spi.StableProfileProvider;
3738
import jdk.graal.compiler.phases.VerifyPhase;
38-
39+
import jdk.graal.compiler.serviceprovider.GraalServices;
3940
import jdk.vm.ci.meta.ResolvedJavaMethod;
4041
import jdk.vm.ci.meta.ResolvedJavaType;
4142

@@ -59,6 +60,7 @@ public class VerifyProfileMethodUsage extends VerifyPhase<CoreProviders> {
5960
ALLOWED_CLASSES.add(StableProfileProvider.CachingProfilingInfo.class);
6061
ALLOWED_CLASSES.add(ResolvedJavaMethodProfileProvider.class);
6162
ALLOWED_CLASSES.add(ResolvedJavaMethod.class);
63+
ALLOWED_CLASSES.add(GraalServices.class);
6264
}
6365

6466
@Override

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/debug/IgvDumpChannel.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@
4242
import jdk.graal.compiler.core.common.NativeImageSupport;
4343
import jdk.graal.compiler.debug.DebugOptions.PrintGraphTarget;
4444
import jdk.graal.compiler.options.OptionValues;
45-
45+
import jdk.graal.compiler.serviceprovider.GlobalAtomicLong;
4646
import jdk.graal.compiler.serviceprovider.GraalServices;
4747

4848
final class IgvDumpChannel implements WritableByteChannel {
@@ -92,7 +92,7 @@ void realClose() throws IOException {
9292
}
9393
}
9494

95-
private static boolean networkDumpingUnsupportedWarned;
95+
private static final GlobalAtomicLong NETWORK_DUMPING_UNSUPPORTED_WARNED = new GlobalAtomicLong("NETWORK_DUMPING_UNSUPPORTED_WARNED", 0L);
9696

9797
WritableByteChannel channel() throws IOException {
9898
if (closed) {
@@ -104,9 +104,9 @@ WritableByteChannel channel() throws IOException {
104104
sharedChannel = createFileChannel(pathProvider, null);
105105
} else if (target == PrintGraphTarget.Network) {
106106
if (NativeImageSupport.inRuntimeCode() && !ENABLE_NETWORK_DUMPING) {
107-
if (!networkDumpingUnsupportedWarned) {
107+
if (NETWORK_DUMPING_UNSUPPORTED_WARNED.get() == 0) {
108108
// Ignore races or multiple isolates - an extra warning is ok
109-
networkDumpingUnsupportedWarned = true;
109+
NETWORK_DUMPING_UNSUPPORTED_WARNED.compareAndSet(0, 1);
110110
TTY.printf("WARNING: Graph dumping to network not supported as the %s system property was false when building - dumping to file instead.%n",
111111
ENABLE_NETWORK_DUMPING_PROP);
112112
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/CompilationTask.java

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,11 @@
3030
import static jdk.graal.compiler.core.GraalCompilerOptions.CompilationFailureAction;
3131
import static jdk.graal.compiler.core.GraalCompilerOptions.PrintCompilation;
3232
import static jdk.graal.compiler.core.phases.HighTier.Options.Inline;
33+
import static jdk.graal.compiler.hotspot.CompilationTask.Options.MethodRecompilationLimit;
3334
import static jdk.graal.compiler.java.BytecodeParserOptions.InlineDuringParsing;
3435

3536
import java.io.PrintStream;
37+
import java.util.ListIterator;
3638

3739
import org.graalvm.collections.EconomicMap;
3840

@@ -60,9 +62,16 @@
6062
import jdk.graal.compiler.nodes.spi.StableProfileProvider.TypeFilter;
6163
import jdk.graal.compiler.options.Option;
6264
import jdk.graal.compiler.options.OptionKey;
65+
import jdk.graal.compiler.options.OptionType;
6366
import jdk.graal.compiler.options.OptionValues;
6467
import jdk.graal.compiler.options.OptionsParser;
68+
import jdk.graal.compiler.phases.BasePhase;
69+
import jdk.graal.compiler.phases.common.DeoptimizationGroupingPhase;
70+
import jdk.graal.compiler.phases.common.ForceDeoptSpeculationPhase;
71+
import jdk.graal.compiler.phases.tiers.MidTierContext;
72+
import jdk.graal.compiler.phases.tiers.Suites;
6573
import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
74+
import jdk.graal.compiler.serviceprovider.GraalServices;
6675
import jdk.vm.ci.code.BailoutException;
6776
import jdk.vm.ci.code.CodeCacheProvider;
6877
import jdk.vm.ci.hotspot.HotSpotCompilationRequest;
@@ -72,6 +81,7 @@
7281
import jdk.vm.ci.hotspot.HotSpotNmethod;
7382
import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
7483
import jdk.vm.ci.meta.JavaTypeProfile;
84+
import jdk.vm.ci.meta.ProfilingInfo;
7585
import jdk.vm.ci.meta.ResolvedJavaMethod;
7686
import jdk.vm.ci.runtime.JVMCICompiler;
7787

@@ -90,6 +100,10 @@ public static class Options {
90100
If the value starts with a non-letter character, that
91101
character is used as the separator between options instead of a space.""")//
92102
public static final OptionKey<String> PerMethodOptions = new OptionKey<>(null);
103+
@Option(help = "Hard limit on the number of recompilations to avoid deopt loops", type = OptionType.Debug)//
104+
public static final OptionKey<Integer> MethodRecompilationLimit = new OptionKey<>(15);
105+
@Option(help = "Try to detect and report the source of deopt loops", type = OptionType.Debug)//
106+
public static final OptionKey<Integer> DetectRecompilationLimit = new OptionKey<>(-1);
93107
}
94108

95109
@Override
@@ -119,6 +133,10 @@ public void onStuckCompilation(CompilationWatchDog watchDog, Thread watched, Com
119133

120134
private final boolean eagerResolving;
121135

136+
protected boolean checkRecompileCycle;
137+
138+
protected final int decompileCount;
139+
122140
/**
123141
* Filter describing which types in {@link JavaTypeProfile} should be considered for profile
124142
* writing. This allows programmatically changing which types are saved.
@@ -167,6 +185,10 @@ protected HotSpotCompilationRequestResult handleException(Throwable t) {
167185
*/
168186
return HotSpotCompilationRequestResult.failure(bailout.getMessage(), !bailout.isPermanent());
169187
}
188+
if (t instanceof ForceDeoptSpeculationPhase.TooManyDeoptimizationsError) {
189+
// Handle this as a permanent bailout
190+
return HotSpotCompilationRequestResult.failure(t.getMessage(), false);
191+
}
170192

171193
/*
172194
* Treat random exceptions from the compiler as indicating a problem compiling this
@@ -272,7 +294,23 @@ protected HotSpotCompilationRequestResult performCompilation(DebugContext debug)
272294

273295
try (DebugContext.Scope s = debug.scope("Compiling", new DebugDumpScope(getIdString(), true))) {
274296
graph = compiler.createGraph(method, entryBCI, profileProvider, compilationId, debug.getOptions(), debug);
275-
result = compiler.compile(graph, shouldRetainLocalVariables, shouldUsePreciseUnresolvedDeopts, eagerResolving, compilationId, debug);
297+
Suites suites = compiler.getSuites(compiler.getGraalRuntime().getHostProviders(), debug.getOptions());
298+
if (checkRecompileCycle && decompileCount < MethodRecompilationLimit.getValue(debug.getOptions())) {
299+
/*
300+
* Disable DeoptimizationGroupingPhase to simplify the creation of the
301+
* speculations for each deopt.
302+
*/
303+
ListIterator<BasePhase<? super MidTierContext>> phase = suites.getMidTier().findPhase(DeoptimizationGroupingPhase.class);
304+
if (phase != null) {
305+
phase.remove();
306+
}
307+
suites.getLowTier().appendPhase(new ForceDeoptSpeculationPhase<>(decompileCount));
308+
}
309+
result = compiler.compile(graph, shouldRetainLocalVariables, shouldUsePreciseUnresolvedDeopts, eagerResolving, compilationId, debug, suites);
310+
if (checkRecompileCycle && decompileCount >= MethodRecompilationLimit.getValue(debug.getOptions())) {
311+
ProfilingInfo info = profileProvider.getProfilingInfo(method);
312+
throw new ForceDeoptSpeculationPhase.TooManyDeoptimizationsError("too many decompiles: " + decompileCount + " " + ForceDeoptSpeculationPhase.getDeoptSummary(info));
313+
}
276314
} catch (Throwable e) {
277315
throw debug.handle(e);
278316
}
@@ -334,6 +372,7 @@ public CompilationTask(HotSpotJVMCIRuntime jvmciRuntime,
334372
this.shouldUsePreciseUnresolvedDeopts = shouldUsePreciseUnresolvedDeopts;
335373
this.eagerResolving = eagerResolving;
336374
this.installAsDefault = installAsDefault;
375+
this.decompileCount = GraalServices.getDecompileCount(request.getMethod());
337376
}
338377

339378
public void setTypeFilter(TypeFilter typeFilter) {
@@ -427,6 +466,10 @@ public int getEntryBCI() {
427466
return getRequest().getEntryBCI();
428467
}
429468

469+
public StableProfileProvider getProfileProvider() {
470+
return profileProvider;
471+
}
472+
430473
/**
431474
* @return the compilation id plus a trailing '%' if the compilation is an OSR to match
432475
* PrintCompilation style output

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/HotSpotGraalCompiler.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
*/
2525
package jdk.graal.compiler.hotspot;
2626

27+
import static jdk.graal.compiler.core.GraalCompilerOptions.CompilationFailureAction;
2728
import static jdk.graal.compiler.core.common.GraalOptions.OptAssumptions;
2829

2930
import java.util.Arrays;
@@ -35,6 +36,7 @@
3536
import jdk.graal.compiler.api.runtime.GraalJVMCICompiler;
3637
import jdk.graal.compiler.code.CompilationResult;
3738
import jdk.graal.compiler.core.CompilationWatchDog;
39+
import jdk.graal.compiler.core.CompilationWrapper;
3840
import jdk.graal.compiler.core.GraalCompiler;
3941
import jdk.graal.compiler.core.common.CompilationIdentifier;
4042
import jdk.graal.compiler.core.common.LibGraalSupport;
@@ -61,10 +63,12 @@
6163
import jdk.graal.compiler.phases.OptimisticOptimizations;
6264
import jdk.graal.compiler.phases.OptimisticOptimizations.Optimization;
6365
import jdk.graal.compiler.phases.PhaseSuite;
66+
import jdk.graal.compiler.phases.common.ForceDeoptSpeculationPhase;
6467
import jdk.graal.compiler.phases.tiers.HighTierContext;
6568
import jdk.graal.compiler.phases.tiers.Suites;
6669
import jdk.graal.compiler.printer.GraalDebugHandlersFactory;
6770
import jdk.graal.compiler.serviceprovider.GlobalAtomicLong;
71+
import jdk.graal.compiler.serviceprovider.GraalServices;
6872
import jdk.internal.misc.Unsafe;
6973
import jdk.vm.ci.code.CompilationRequest;
7074
import jdk.vm.ci.code.CompilationRequestResult;
@@ -156,9 +160,31 @@ public CompilationRequestResult compileMethod(CompilationRequest request, boolea
156160
}
157161
}
158162
}
163+
159164
HotSpotCompilationRequest hsRequest = (HotSpotCompilationRequest) request;
160165
CompilationTask task = new CompilationTask(jvmciRuntime, this, hsRequest, true, shouldRetainLocalVariables(hsRequest.getJvmciEnv()), shouldUsePreciseUnresolvedDeopts(), installAsDefault);
161166
OptionValues options = task.filterOptions(initialOptions);
167+
int decompileCount = GraalServices.getDecompileCount(task.getMethod());
168+
if (decompileCount != -1) {
169+
if (decompileCount >= CompilationTask.Options.MethodRecompilationLimit.getValue(options)) {
170+
if (CompilationFailureAction.getValue(options) == CompilationWrapper.ExceptionAction.Diagnose) {
171+
// If Diagnose is enabled then allow the compile to proceed and throw an
172+
// exception after wards to allow the retry machinery to capture a graph.
173+
task.checkRecompileCycle = true;
174+
} else {
175+
// Treat this as a permanent bailout. This is similar to HotSpots
176+
// PerMethodRecompilationCutoff flag but since it's under our control we can
177+
// produce more useful diagnostics. The default HotSpot limit of 400 is
178+
// probably too large as well.
179+
ProfilingInfo info = task.getProfileProvider().getProfilingInfo(request.getMethod());
180+
return HotSpotCompilationRequestResult.failure("too many decompiles: " + decompileCount + " " + ForceDeoptSpeculationPhase.getDeoptSummary(info), false);
181+
}
182+
183+
} else if (CompilationTask.Options.DetectRecompilationLimit.getValue(options) != -1 &&
184+
decompileCount >= CompilationTask.Options.DetectRecompilationLimit.getValue(options)) {
185+
task.checkRecompileCycle = true;
186+
}
187+
}
162188

163189
HotSpotVMConfigAccess config = new HotSpotVMConfigAccess(graalRuntime.getVMConfig().getStore());
164190
LibGraalSupport libgraal = LibGraalSupport.INSTANCE;
@@ -247,15 +273,14 @@ public StructuredGraph createGraph(ResolvedJavaMethod method, int entryBCI, Prof
247273

248274
@SuppressWarnings("try")
249275
public CompilationResult compileHelper(CompilationResultBuilderFactory crbf, CompilationResult result, StructuredGraph graph, boolean shouldRetainLocalVariables,
250-
boolean shouldUsePreciseUnresolvedDeopts, boolean eagerResolving, OptionValues options) {
276+
boolean shouldUsePreciseUnresolvedDeopts, boolean eagerResolving, Suites suites, OptionValues options) {
251277
int entryBCI = graph.getEntryBCI();
252278
ResolvedJavaMethod method = graph.method();
253279
assert options == graph.getOptions() : Assertions.errorMessage(options, graph.getOptions());
254280
HotSpotBackend backend = graalRuntime.getHostBackend();
255281
HotSpotProviders providers = backend.getProviders();
256282
final boolean isOSR = entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
257283

258-
Suites suites = getSuites(providers, options);
259284
LIRSuites lirSuites = getLIRSuites(providers, options);
260285
ProfilingInfo profilingInfo = graph.getProfileProvider() != null ? graph.getProfileProvider().getProfilingInfo(method, !isOSR, isOSR) : DefaultProfilingInfo.get(TriState.FALSE);
261286
OptimisticOptimizations optimisticOpts = getOptimisticOpts(profilingInfo, options);
@@ -300,9 +325,10 @@ public CompilationResult compile(StructuredGraph graph,
300325
boolean shouldUsePreciseUnresolvedDeopts,
301326
boolean eagerResolving,
302327
CompilationIdentifier compilationId,
303-
DebugContext debug) {
328+
DebugContext debug,
329+
Suites suites) {
304330
CompilationResult result = new CompilationResult(compilationId);
305-
return compileHelper(CompilationResultBuilderFactory.Default, result, graph, shouldRetainLocalVariables, shouldUsePreciseUnresolvedDeopts, eagerResolving, debug.getOptions());
331+
return compileHelper(CompilationResultBuilderFactory.Default, result, graph, shouldRetainLocalVariables, shouldUsePreciseUnresolvedDeopts, eagerResolving, suites, debug.getOptions());
306332
}
307333

308334
protected OptimisticOptimizations getOptimisticOpts(ProfilingInfo profilingInfo, OptionValues options) {

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/DeoptimizeNode.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ public final class DeoptimizeNode extends AbstractDeoptimizeNode implements Lowe
4646
protected DeoptimizationAction action;
4747
protected DeoptimizationReason reason;
4848
protected int debugId;
49-
protected final Speculation speculation;
49+
protected Speculation speculation;
5050
protected boolean mayConvertToGuard;
5151

5252
public DeoptimizeNode(DeoptimizationAction action, DeoptimizationReason reason) {
@@ -161,4 +161,9 @@ public void mayConvertToGuard(boolean newMayConvertToGuard) {
161161

162162
@NodeIntrinsic
163163
public static native void deopt(@ConstantNodeParameter DeoptimizationAction action, @ConstantNodeParameter DeoptimizationReason reason);
164+
165+
public void setSpeculation(Speculation speculate) {
166+
assert speculation.equals(SpeculationLog.NO_SPECULATION);
167+
speculation = speculate;
168+
}
164169
}

compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/nodes/StructuredGraph.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@
7272
import jdk.vm.ci.code.BytecodeFrame;
7373
import jdk.vm.ci.meta.Assumptions;
7474
import jdk.vm.ci.meta.Assumptions.Assumption;
75+
import jdk.vm.ci.meta.DeoptimizationReason;
7576
import jdk.vm.ci.meta.JavaMethod;
7677
import jdk.vm.ci.meta.ProfilingInfo;
7778
import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -501,6 +502,17 @@ public void getDebugProperties(Map<Object, Object> properties) {
501502
properties.put("compilationIdentifier", compilationId());
502503
properties.put("edgeModificationCount", getEdgeModificationCount());
503504
properties.put("assumptions", String.valueOf(getAssumptions()));
505+
if (method() != null && profileProvider != null) {
506+
ProfilingInfo profilingInfo = profileProvider.getProfilingInfo(method());
507+
for (DeoptimizationReason reason : DeoptimizationReason.values()) {
508+
if (reason != DeoptimizationReason.None) {
509+
int count = profilingInfo.getDeoptimizationCount(reason);
510+
if (count != 0) {
511+
properties.put("DeoptimizationCount-" + reason, count);
512+
}
513+
}
514+
}
515+
}
504516
}
505517

506518
@Override

0 commit comments

Comments
 (0)