Skip to content

Commit e7bc0ca

Browse files
committed
[GR-58163] Replay compilation.
PullRequest: graal/18820
2 parents b887c36 + b3a669c commit e7bc0ca

File tree

77 files changed

+10993
-156
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+10993
-156
lines changed

compiler/CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
This changelog summarizes newly introduced optimizations and other compiler related changes.
44

5+
## GraalVM for JDK 26 (Internal Version 26.0.0)
6+
* (GR-58163): Added support for recording and replaying JIT compilations. The `-Djdk.graal.RecordForReplay=*` option
7+
serializes all compilations matching the pattern to JSON files, which contain the results of JVMCI calls. The
8+
recorded compilations can be replayed with the `mx replaycomp` command. Truffle compilations are currently not
9+
supported. See `docs/ReplayCompilation.md` for details.
10+
511
## GraalVM for JDK 25 (Internal Version 25.0.0)
612
* (GR-60088): This PR adds the `org.graalvm.nativeimage.libgraal` SDK module. With this module, all logic for building
713
libgraal has been moved into the compiler suite in a new `jdk.graal.compiler.libgraal` module

compiler/docs/ReplayCompilation.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Replay Compilation
2+
3+
The GraalVM compiler can record the inputs to a compilation task, serialize these inputs into a JSON file, and reproduce
4+
the compilation using the same inputs. Replay compilation is based on instrumenting the JVM Compiler Interface (JVMCI).
5+
Truffle compilations are currently not supported. It is not a goal to execute the replayed code.
6+
7+
This file is a manual from the user's perspective. To learn how replay compilation is implemented, start by reading
8+
`ReplayCompilationSupport.java` and `CompilerInterfaceDeclarations.java`.
9+
10+
## Example
11+
12+
Recording is enabled with the option `-Djdk.graal.RecordForReplay=*`. The value is a method filter selecting the methods
13+
to be recorded. The syntax of method filters is explained `MethodFilter.java`.
14+
15+
The below command records and serializes every compilation into `./replay-files/replaycomp/<compile-id>.json`.
16+
The directories are created if they do not exist.
17+
18+
```shell
19+
mx benchmark renaissance:scrabble -- -Djdk.graal.RecordForReplay='*' -Djdk.graal.DumpPath=$PWD/replay-files -- -r 1
20+
```
21+
22+
It is recommended to select specific methods rather than `*` to avoid slowing the compiler down and producing gigabytes
23+
of data. The compile speed overhead for recorded methods can be on the order of 10x. The size of a typical compilation
24+
unit is between 1 and 10 MB but compression saves 95% of space. Note that if the VM exits during an ongoing compilation,
25+
some of the JSON files may be incomplete.
26+
27+
The `mx replaycomp` command finds all JSON replay files found in a given directory (and subdirectories) and invokes
28+
the replay compiler on each. The command also accepts a path to a single file.
29+
30+
```shell
31+
mx replaycomp ./replay-files
32+
```
33+
34+
## Debugging
35+
36+
A replayed compilation can be debugged using a standard Java debugger (see `Debugging.md`).
37+
38+
```shell
39+
mx -d replaycomp ./replay-files
40+
```
41+
42+
## Record Crashed Compilations
43+
44+
Using the `-Djdk.graal.CompilationFailureAction=Diagnose` option, the compiler retries and records crashed compilations.
45+
The command below exercises this behavior by forcing a crash. The JSON replay file can then be found in the `replaycomp`
46+
directory inside the diagnostic zip archive.
47+
48+
```shell
49+
mx benchmark renaissance:scrabble -- -Djdk.graal.CompilationFailureAction=Diagnose -Djdk.graal.CrashAt=hashCode -- -r 1
50+
```
51+
52+
It is possible to **not** record retried compilations with the option `-Djdk.graal.DiagnoseOptions=RecordForReplay=`.
53+
54+
## Replay with JVM Arguments
55+
56+
JVM arguments, including compiler options, can be passed directly to the `replaycomp` command.
57+
58+
```shell
59+
mx replaycomp -Djdk.graal.Dump=:1 ./replay-files
60+
```
61+
62+
Any `-ea`, `-esa`, and `-X` arguments from the command line are passed to the JVM as well.
63+
64+
## Jargraal vs. Libgraal
65+
66+
Both jargraal and libgraal compilations can be replayed on jargraal. When jargraal replays a libgraal compilation,
67+
it uses encoded snippets to match the behavior and compilations results of libgraal. It is also possible to replay
68+
libgraal compilations on libgraal. Replaying jargraal compilations on libgraal is not supported.
69+
70+
It is necessary to explicitly enable the replay launcher entry point when building libgraal using the VM argument
71+
`-Ddebug.jdk.graal.enableReplayLauncher=true`.
72+
73+
```shell
74+
EXTRA_IMAGE_BUILDER_ARGUMENTS=-Ddebug.jdk.graal.enableReplayLauncher=true mx --env libgraal build
75+
```
76+
77+
When the `--libgraal` argument is passed to `mx replaycomp`, the previously built libgraal native library is loaded, and
78+
the native launcher is invoked instead of the Java launcher. With the below command, all replay related processing
79+
(including JSON parsing) is performed by libgraal code.
80+
81+
```shell
82+
mx replaycomp --libgraal ./replay-files
83+
```
84+
85+
## Replay Options
86+
87+
`--compare-graphs=true` compares the final canonical graph of the replayed compilation to the recorded one, which is
88+
included in the JSON replay file. If there is a mismatch, the command exits with a non-zero status.
89+
90+
```shell
91+
mx replaycomp --compare-graphs=true ./replay-files
92+
```
93+
94+
If the replayed compilation diverges from the recorded one, the compiler may query JVMCI information that is not
95+
recorded in the JSON file. The default behavior is to return default values, which may not cause a compiler crash.
96+
The `-Djdk.graal.ReplayDivergenceIsFailure=true` argument prevents using default values and instead causes a crash.
97+
98+
```shell
99+
mx replaycomp -Djdk.graal.ReplayDivergenceIsFailure=true ./replay-files
100+
```

compiler/mx.compiler/mx_compiler.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1571,6 +1571,41 @@ def profdiff(args):
15711571
vm_args = ['-cp', cp, 'org.graalvm.profdiff.Profdiff'] + args
15721572
return jdk.run_java(args=vm_args)
15731573

1574+
def replaycomp_vm_args():
1575+
"""Returns the VM arguments required to run the replay compilation launcher."""
1576+
vm_args = [
1577+
'-XX:-UseJVMCICompiler',
1578+
'--enable-native-access=org.graalvm.truffle',
1579+
'--add-exports=java.base/jdk.internal.module=ALL-UNNAMED',
1580+
'-Djdk.graal.CompilationFailureAction=Print'
1581+
]
1582+
_, dists = mx.defaultDependencies(opt_limit_to_suite=True)
1583+
dists = [d for d in dists if d.isJARDistribution() and os.path.exists(d.classpath_repr(resolve=False))]
1584+
return mx.get_runtime_jvm_args(dists) + vm_args
1585+
1586+
def replaycomp_main_class():
1587+
"""Returns the main class name for the replay compilation launcher."""
1588+
return 'jdk.graal.compiler.hotspot.replaycomp.test.ReplayCompilationLauncher'
1589+
1590+
def replaycomp(args):
1591+
"""Runs the replay compilation launcher with the provided launcher and VM arguments."""
1592+
extra_vm_args = []
1593+
launcher_args = []
1594+
for arg in args:
1595+
vm_arg_prefixes = ['-X', '-D', '-ea', '-enableassertions', '-esa', '-enablesystemassertions']
1596+
if any(map(arg.startswith, vm_arg_prefixes)):
1597+
extra_vm_args.append(arg)
1598+
elif arg == '--libgraal':
1599+
jvmci_lib_path = os.path.join(mx.suite('sdk').get_output_root(platformDependent=True, jdkDependent=False),
1600+
mx.add_lib_suffix(mx.add_lib_prefix('jvmcicompiler')) + '.image')
1601+
extra_vm_args.extend([
1602+
'-XX:+UseJVMCINativeLibrary',
1603+
f'-XX:JVMCILibPath={jvmci_lib_path}'
1604+
])
1605+
else:
1606+
launcher_args.append(arg)
1607+
return run_vm([*replaycomp_vm_args(), *extra_vm_args, replaycomp_main_class(), *launcher_args], nonZeroIsFatal=False)
1608+
15741609
def igvutil(args):
15751610
"""various utilities to inspect and modify IGV graphs"""
15761611
cp = mx.classpath('GRAAL_IGVUTIL', jdk=jdk)
@@ -1589,6 +1624,7 @@ def igvutil(args):
15891624
'graaljdk-show': [print_graaljdk_config, '[options]'],
15901625
'phaseplan-fuzz-jtt-tests': [phaseplan_fuzz_jtt_tests, "Runs JTT's unit tests with fuzzed phase plans."],
15911626
'profdiff': [profdiff, '[options] proftool_output1 optimization_log1 proftool_output2 optimization_log2'],
1627+
'replaycomp': [replaycomp, ''],
15921628
'igvutil': [igvutil, '[subcommand] [options]'],
15931629
})
15941630

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalEntryPoints.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.io.PrintStream;
3232
import java.util.Arrays;
3333
import java.util.Map;
34+
import java.util.function.BooleanSupplier;
3435

3536
import org.graalvm.collections.EconomicMap;
3637
import org.graalvm.jniutils.JNI.JNIEnv;
@@ -45,12 +46,15 @@
4546

4647
import jdk.graal.compiler.debug.GlobalMetrics;
4748
import jdk.graal.compiler.debug.GraalError;
49+
import jdk.graal.compiler.debug.TTY;
4850
import jdk.graal.compiler.hotspot.CompilationContext;
4951
import jdk.graal.compiler.hotspot.CompilationTask;
5052
import jdk.graal.compiler.hotspot.HotSpotGraalCompiler;
5153
import jdk.graal.compiler.hotspot.HotSpotGraalRuntime;
5254
import jdk.graal.compiler.hotspot.HotSpotGraalServices;
5355
import jdk.graal.compiler.hotspot.ProfileReplaySupport;
56+
import jdk.graal.compiler.hotspot.replaycomp.ReplayCompilationRunner;
57+
import jdk.graal.compiler.hotspot.replaycomp.ReplayCompilationSupport;
5458
import jdk.graal.compiler.options.OptionDescriptors;
5559
import jdk.graal.compiler.options.OptionKey;
5660
import jdk.graal.compiler.options.OptionValues;
@@ -288,4 +292,44 @@ private static long hashConstantOopFields(JNIEnv jniEnv,
288292
return 0;
289293
}
290294
}
295+
296+
/**
297+
* Runs the replay compilation launcher in libgraal with the provided command-line arguments.
298+
*
299+
* @param argBuffer a native buffer containing a zero-terminated UTF-8 string of
300+
* {@code '\n'}-separated arguments for the replay compilation launcher
301+
* @return the exit status of the replay compilation launcher
302+
*/
303+
@SuppressWarnings({"unused", "try"})
304+
@CEntryPoint(name = "Java_jdk_graal_compiler_hotspot_replaycomp_test_ReplayCompilationLauncher_runInLibgraal", include = LibGraalReplayLauncherEnabled.class)
305+
private static int replayCompilation(JNIEnv jniEnv,
306+
PointerBase jclass,
307+
@IsolateThreadContext long isolateThread,
308+
long argBuffer) {
309+
try (JNIMethodScope scope = new JNIMethodScope("replayCompilation", jniEnv)) {
310+
String argString = CTypeConversion.utf8ToJavaString(Word.pointer(argBuffer));
311+
String[] args;
312+
if (argString.isEmpty()) {
313+
args = new String[0];
314+
} else {
315+
args = argString.split("\n");
316+
}
317+
return ReplayCompilationRunner.run(args, TTY.out().out()).getStatus();
318+
} catch (Throwable t) {
319+
JNIExceptionWrapper.throwInHotSpot(jniEnv, t);
320+
return ReplayCompilationRunner.ExitStatus.Failure.getStatus();
321+
} finally {
322+
LibGraalSupportImpl.doReferenceHandling();
323+
}
324+
}
325+
326+
/**
327+
* Controls whether the replay launcher entry point should be included in libgraal.
328+
*/
329+
private static final class LibGraalReplayLauncherEnabled implements BooleanSupplier {
330+
@Override
331+
public boolean getAsBoolean() {
332+
return new LibGraalFeature.IsEnabled().getAsBoolean() && ReplayCompilationSupport.ENABLE_REPLAY_LAUNCHER;
333+
}
334+
}
291335
}

compiler/src/jdk.graal.compiler.libgraal/src/jdk/graal/compiler/libgraal/LibGraalFeature.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import java.util.function.Consumer;
4545
import java.util.stream.Collectors;
4646

47+
import jdk.graal.compiler.serviceprovider.GraalServices;
4748
import org.graalvm.collections.EconomicMap;
4849
import org.graalvm.jniutils.NativeBridgeSupport;
4950
import org.graalvm.nativeimage.ImageInfo;
@@ -315,7 +316,11 @@ public void accept(DuringAnalysisAccess duringAnalysisAccess) {
315316

316317
private void registerHostedOnlyElements(BeforeAnalysisAccess access, AnnotatedElement... elements) {
317318
for (AnnotatedElement element : elements) {
318-
if (element.getAnnotation(HostedOnly.class) != null) {
319+
HostedOnly annotation = element.getAnnotation(HostedOnly.class);
320+
if (annotation == null) {
321+
continue;
322+
}
323+
if (annotation.unlessTrue().isEmpty() || !Boolean.parseBoolean(GraalServices.getSavedProperty(annotation.unlessTrue()))) {
319324
access.registerReachabilityHandler(new HostedOnlyElementCallback(element, reachedHostedOnlyElements), element);
320325
}
321326
}

0 commit comments

Comments
 (0)