Skip to content

Commit c03649a

Browse files
GiuntaJgergo-
authored andcommitted
[GR-32314] Phase plan fuzzing.
1 parent 0d44bf3 commit c03649a

File tree

20 files changed

+2204
-104
lines changed

20 files changed

+2204
-104
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*.obj
1616
*.orig
1717
*.pdf
18+
*.phaseplan
1819
*.pyc
1920
*.rej
2021
*.swn

compiler/ci_common/gate.jsonnet

+7-1
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@
5858
},
5959

6060
test_javabase:: s.base("build,javabasetest"),
61-
61+
test_jtt_phaseplan_fuzzing:: s.base("build,phaseplan-fuzz-jtt-tests"),
6262
test_vec16:: s.base(extra_vm_args="-Dgraal.DetailedAsserts=true -XX:MaxVectorSize=16"),
6363
test_avx0:: s.base(extra_vm_args="-Dgraal.ForceAdversarialLayout=true", jvm_config_suffix="-avx0"),
6464
test_avx1:: s.base(extra_vm_args="-Dgraal.ForceAdversarialLayout=true", jvm_config_suffix="-avx1"),
@@ -78,6 +78,7 @@
7878
ctw:: s.base("build,ctw", no_warning_as_error=true),
7979

8080
ctw_economy:: s.base("build,ctweconomy", extra_vm_args="-Dgraal.CompilerConfiguration=economy"),
81+
ctw_phaseplan_fuzzing:: s.base("build,ctwphaseplanfuzzing"),
8182

8283
coverage_ctw:: s.base("build,ctw", ["--jacoco-omit-excluded", "--jacocout", "html"], extra_vm_args="-DCompileTheWorld.MaxClasses=5000" /*GR-23372*/) + {
8384
run+: [
@@ -186,6 +187,8 @@
186187
# Each value in this map is an object that overrides or extends the
187188
# fields of the denoted build.
188189
local weeklies = {
190+
"weekly-compiler-ctw_phaseplan_fuzzing-labsjdk-17-linux-amd64": {},
191+
189192
"weekly-compiler-test-labsjdk-11-windows-amd64": t("55:00"),
190193
"weekly-compiler-test-labsjdk-11-darwin-amd64": {},
191194
"weekly-compiler-test-labsjdk-11-darwin-aarch64": {},
@@ -194,6 +197,7 @@
194197
"weekly-compiler-test_avx0-labsjdk-17-linux-amd64": {},
195198
"weekly-compiler-test_avx1-labsjdk-17-linux-amd64": {},
196199
"weekly-compiler-test_javabase-labsjdk-17-linux-amd64": {},
200+
"weekly-compiler-test_jtt_phaseplan_fuzzing-labsjdk-17-linux-amd64": {},
197201
"weekly-compiler-benchmarktest-labsjdk-17Debug-linux-amd64": {},
198202

199203
"weekly-compiler-coverage*": {},
@@ -305,10 +309,12 @@
305309
# Builds run on only on linux-amd64-jdk17
306310
local linux_amd64_jdk17_builds = [self.make_build("17", "linux-amd64", task).build
307311
for task in [
312+
"ctw_phaseplan_fuzzing",
308313
"test_vec16",
309314
"test_avx0",
310315
"test_avx1",
311316
"test_javabase",
317+
"test_jtt_phaseplan_fuzzing",
312318
"style"
313319
]
314320
],
+174
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
# Compilation plan fuzzing
2+
This page covers the creation and usage of fuzzed compilation plans instead of the usual phase orderings specified by the different `CompilerConfiguration`s.
3+
## Abstraction design
4+
The following design can be found in the package `org.graalvm.compiler.core.phases.fuzzing`.
5+
### Diagram
6+
```
7+
Suites > FuzzedSuites
8+
|
9+
AbstractCompilationPlan > MinimalFuzzedCompilationPlan > FullFuzzedCompilationPlan
10+
/ | \ 3x| 3x|
11+
AbstractTierPlan AbstractTierPlan AbstractTierPlan > MinimalFuzzedTierPlan > FullFuzzedTierPlan
12+
```
13+
`A > B` means A is the superclass of B.
14+
Vertical lines (`/`, `|` and `\`) means the lower end of the line is a field of the higher end of the line.
15+
16+
### `AbstractCompilationPlan`
17+
An `AbstractCompilationPlan` represents a specific ordering of phases.
18+
It is composed of three `AbstractTierPlan`s which represent the phase orderings for high tier, mid tier and low tier.
19+
20+
### `AbstractTierPlan`
21+
An `AbstractTierPlan` represents the phase ordering for a tier (high, mid or low tier).
22+
It contains a `PhaseSuite` which specifies the specific phase ordering of the plan.
23+
24+
### `MinimalFuzzedCompilationPlan`
25+
A `MinimalFuzzedCompilationPlan` is a fuzzed compilation plan that is composed only of phases that apply a mandatory stage or that fulfills a future stage requirement. It is a phase ordering with the least number of phases possible.
26+
27+
A mandatory stage (see `GraphState.MandatoryStages`) is a stage that necessarily needs to be applied to the graph to produce a valid compilation. For example, `StageFlag.HIGH_TIER_LOWERING` is required in high tier to transition to mid tier.
28+
29+
A future stage requirement means the graph requires a stage to be applied. For example, if vector nodes are created, the graph requires a stage that lowers this kind of nodes.
30+
31+
A `MinimalFuzzedCompilationPlan` is composed of three `MinimalFuzzedTierPlan`s which represent the phase orderings for high tier, mid tier and low tier.
32+
33+
### `FullFuzzedCompilationPlan`
34+
A `FullFuzzedCompilationPlan` represents a compilation plan created by fuzzing.
35+
It is created by inserting optional phases in a `MinimalFuzzedCompilationPlan`.
36+
It is composed of three `FullFuzzedTierPlan`s which represent the phase orderings for high tier, mid tier and low tier.
37+
38+
### `FuzzedSuites`
39+
`FuzzedSuites` is a subclass of `Suites` and represents suites created using a `FullFuzzedCompilationPlan`.
40+
Since it is a subclass of `Suites`, it can be used instead of `Suites` to provide fuzzed phase orderings.
41+
42+
## Fuzzing strategy
43+
To create a fuzzed compilation plan, three fuzzed tier plans are constructed: one for high, mid and low tier.
44+
They are assembled in this order to ensure the graph state resulting from a previous tier is carried to the next tier. This way, future stage requirements can be resolved by phases in later tiers.
45+
46+
### Minimal fuzzed compilation plan
47+
The `MinimalFuzzedCompilationPlan` is created by constructing three `MinimalFuzzedTierPlan`s. These tier plans are assembled in three steps:
48+
1. Insert in the fuzzed tier plan all the phases for which `BasePhase.mustApply(GraphState)` returns `true`.
49+
This resolves graph or stage requirements that were introduced by earlier tiers.
50+
2. Loop over the phases that apply a mandatory stage and try to insert them in the tier plan.
51+
If the mandatory stages cannot be reached after a fixed number of attempts, an error is thrown.
52+
3. Insert the phases which `BasePhase.mustApply(GraphState)` after applying the tier plan resulting from step 2.
53+
This leaves only future stage requirements remaining that cannot be fulfilled by the tier itself.
54+
55+
### Inserting phases that must apply
56+
To fulfill future stage requirements, phases for which `BasePhase.mustApply(GraphState)` returns `true` are inserted in the suite. The procedure is as follow:
57+
```
58+
queue = queueContainingAllThePhases();
59+
while(queue.contains(phaseThatMustApply)){
60+
// Try to insert the phase in the plan, starting from the end.
61+
for(position=endPosition; position >= startPosition; position--){
62+
if(!phaseMustApply(position)){
63+
break;
64+
}
65+
if(canInsertPhaseAtPosition(phaseThatMustApply, position)){
66+
insertPhaseAtPosition(phaseThatMustApply, position);
67+
break;
68+
}
69+
}
70+
// If the phase cannot be inserted, it means it must happen after another phase. Try again later.
71+
queue.putAtTheEnd(phaseThatMustApply);
72+
}
73+
```
74+
75+
### Full fuzzed compilation plan
76+
The `FullFuzzedCompilationPlan` is created by constructing three `FullFuzzedTierPlan`s. These tier plans are assembled with the following steps:
77+
1. Initialize the plan to given `MinimalFuzzedTierPlan`.
78+
2. Insert in the fuzzed tier plan all the phases for which `BasePhase.mustApply(GraphState)` returns `true`.
79+
This resolves graph or stage requirements that were introduced by earlier tiers.
80+
3. Insert optional phases that can be applied at most once. These phases are the ones that modify the graph state.
81+
Each phase is inserted by following the logic described by this pseudo-code:
82+
```
83+
phase = pickRandomPhase()
84+
for randomPosition in positionsInThePlan {
85+
if(skipped){
86+
continue;
87+
}
88+
newPlan = currPlan.insertPhaseAtPosition(randomPosition, phase);
89+
if(newPlan.respectsOrderingConstraints()){
90+
currPlan = newPlan;
91+
break;
92+
}
93+
}
94+
```
95+
The probability of skipping a phase is determined by a parameter of `FullFuzzedTierPlan`.
96+
97+
4. Insert optional phases that can be applied multiple times since they do not modify the graph state.
98+
Each phase is inserted by following the logic described by this pseudo-code:
99+
```
100+
phase = pickRandomPhase()
101+
for position in positionsInThePlan {
102+
if(skipped){
103+
continue;
104+
}
105+
newPlan = currPlan.insertPhaseAtPosition(position, phase);
106+
if(newPlan.respectsOrderingConstraints()){
107+
currPlan = newPlan;
108+
}
109+
}
110+
```
111+
The probability of skipping a phase is determined by a parameter of `FullFuzzedTierPlan`.
112+
Compared to step 3, we keep on trying to insert the phase in the plan even if the phase was already inserted successfully. Furthermore, we do not insert at a random position but follow the natural positions order.
113+
114+
5. Insert the phases which `BasePhase.mustApply(GraphState)` after applying the tier plan resulting from the previous step.
115+
This leaves only future stage requirements remaining that cannot be fulfilled by the tier itself.
116+
117+
## Reproducibility
118+
### JTT tests
119+
#### Create new fuzzed compilation plans
120+
You can create fuzzed compilation plans for JTT tests. For this, use one of the following equivalent commands:
121+
- `mx fuzz-jtt-tests`
122+
- `mx gate --tags fuzz-jtt-tests`
123+
- `mx unittest -Dtest.graal.compilationplan.fuzzing=true`
124+
125+
It is possible to fix some parameters of the creation of fuzzed compilation plans:
126+
- To test only the minimal fuzzed compilation plan:
127+
* `mx fuzz-jtt-tests --minimal`
128+
* `mx gate --extra-unittest-argument='--minimal' --tags fuzz-jtt-tests`
129+
* `mx fuzz-jtt-tests -Dtest.graal.compilationplan.fuzzing.minimal=true`
130+
* `mx gate --extra-unittest-argument='-Dtest.graal.compilationplan.fuzzing.minimal=true' --tags fuzz-jtt-tests`
131+
* `mx gate --extra-vm-argument='-Dtest.graal.compilationplan.fuzzing.minimal=true' --tags fuzz-jtt-tests`
132+
- You can choose the seed to be used to create `Random` instances. The option is `-Dtest.graal.compilationplan.fuzzing.seed=<seed>` and the short version is `--seed=<seed>`. It can be used like the option for the minimal fuzzed compilation plan.
133+
- To regulate the probability that a phase will be inserted at each position in the suite, you can use:
134+
* `-Dtest.graal.skip.phase.insertion.odds=<number>` or `--skip-phase-odds=<number>`.
135+
- When we try to insert the phase, the phase will be inserted with probability `1/<number>` at each position in the current suite but only if the ordering constraints are respected.
136+
- The creation of the fuzzed compilation plan will use the same odds for each tier.
137+
* `-Dtest.graal.skip.phase.insertion.odds.high.tier=<number>` or `--high-tier-skip-phase=<number>`
138+
- This will determine the probability for the insertion in high tier.
139+
- If the odds for mid tier or low tier are not given, these odds will be used.
140+
- If the odds for high tier are not defined, the default odds defined in `FullFuzzedCompilationPlan` are used instead.
141+
* `-Dtest.graal.skip.phase.insertion.odds.mid.tier=<number>` or `--mid-tier-skip-phase=<number>`
142+
- This will determine the probability for the insertion in mid tier.
143+
* `-Dtest.graal.skip.phase.insertion.odds.low.tier=<number>` or `--low-tier-skip-phase=<number>`
144+
- This will determine the probability for the insertion in low tier.
145+
- You can specify which test you want to run like this:
146+
* `mx fuzz-jtt-tests HP_life`
147+
* `mx gate --extra-unittest-argument='HP_life' --tags fuzz-jtt-tests`
148+
- If you want to use the phases of a specific compiler configuration and respect its requirements, you should use:
149+
* `-Dgraal.CompilerConfiguration=<config>`
150+
151+
#### Load a phase plan
152+
You can load a phase plan (one created by a fuzzed compilation plan or any other phase plan serialized using the `PhasePlanSerializer`) using the command:
153+
```
154+
mx unittest -Dtest.graal.phaseplan.file="/path/to/phaseplan"
155+
```
156+
157+
### CompileTheWorld
158+
#### Create new fuzzed compilation plans
159+
You can use fuzzed plans for `CompileTheWorld`'s compilations by using the following commands:
160+
```
161+
mx gate --tags ctwfuzzing
162+
```
163+
or
164+
```
165+
mx gate --extra-vm-argument='-DCompileTheWorld.FuzzPhasePlan=true' --tags ctw
166+
```
167+
168+
These commands will make each thread create a new fuzzed compilation plan for each compilation they have to perform.
169+
170+
#### Load a phase plan
171+
It is possible to load a phase plan (one created by a fuzzed compilation plan or any other phase plan serialized using the `PhasePlanSerializer`) and use it for the compilation of a method by using the command:
172+
```
173+
mx gate --extra-vm-argument='-DCompileTheWorld.LoadPhasePlan=/path/to/phaseplan' --extra-vm-argument='-DCompileTheWorld.MethodFilter=<methodName>' --extra-vm-argument='-Dgraal.CompilerConfiguration=<config>' --tags ctw
174+
```

compiler/mx.compiler/mx_compiler.py

+58-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
from mx import SafeDirectoryUpdater
4848

4949
import mx_unittest
50-
from mx_unittest import unittest
50+
from mx_unittest import unittest, parse_split_args
5151

5252
from mx_javamodules import as_java_module
5353
from mx_updategraalinopenjdk import updategraalinopenjdk
@@ -346,7 +346,9 @@ class GraalTags:
346346
benchmarktest = ['benchmarktest', 'fulltest']
347347
ctw = ['ctw', 'fulltest']
348348
ctweconomy = ['ctweconomy', 'economy', 'fulltest']
349+
ctwfuzzing = ['ctwfuzzing']
349350
doc = ['javadoc']
351+
fuzz_jtt_tests = ['fuzz-jtt-tests']
350352

351353
def _remove_empty_entries(a):
352354
"""Removes empty entries. Return value is always a list."""
@@ -498,6 +500,7 @@ def compiler_gate_runner(suites, unit_test_runs, bootstrap_tests, tasks, extraVM
498500
'-DCompileTheWorld.MultiThreaded=true', '-Dgraal.InlineDuringParsing=false', '-Dgraal.TrackNodeSourcePosition=true', '-Dgraal.VerifyPhasePlan=true',
499501
'-DCompileTheWorld.Verbose=false', '-XX:ReservedCodeCacheSize=300m',
500502
]
503+
ctw_fuzzing_flags = ['-DCompileTheWorld.FuzzPhasePlan=true', '-Dgraal.PrintGraphStateDiff=true']
501504
with Task('CTW:hosted', tasks, tags=GraalTags.ctw, report=True) as t:
502505
if t:
503506
ctw(ctw_flags, _remove_empty_entries(extraVMarguments))
@@ -507,6 +510,10 @@ def compiler_gate_runner(suites, unit_test_runs, bootstrap_tests, tasks, extraVM
507510
if t:
508511
ctw(ctw_flags + _graalEconomyFlags, _remove_empty_entries(extraVMarguments))
509512

513+
with Task('CTWFuzzing:hosted', tasks, tags=GraalTags.ctwfuzzing, report=True) as t:
514+
if t:
515+
ctw(ctw_flags + ctw_fuzzing_flags, _remove_empty_entries(extraVMarguments))
516+
510517
# bootstrap tests
511518
for b in bootstrap_tests:
512519
b.run(tasks, extraVMarguments)
@@ -519,6 +526,10 @@ def compiler_gate_runner(suites, unit_test_runs, bootstrap_tests, tasks, extraVM
519526
# metadata package was deprecated, exclude it
520527
if t: mx.javadoc(['--exclude-packages', 'com.oracle.truffle.dsl.processor.java'], quietForNoPackages=True)
521528

529+
with Task('JTTFuzzing', tasks, tags=GraalTags.fuzz_jtt_tests, report=True) as t:
530+
if t:
531+
fuzz_jtt_tests([], extraVMarguments=_remove_empty_entries(extraVMarguments), extraUnitTestArguments=_remove_empty_entries(extraUnitTestArguments))
532+
522533
def compiler_gate_benchmark_runner(tasks, extraVMarguments=None, prefix=''):
523534
# run DaCapo benchmarks #
524535
#########################
@@ -1180,6 +1191,51 @@ def javadoc(args):
11801191
args.append('com.oracle.truffle.api.metadata')
11811192
mx.javadoc(args, quietForNoPackages=True)
11821193

1194+
def fuzz_jtt_tests(args, extraVMarguments=None, extraUnitTestArguments=None):
1195+
"""runs JTT unit tests with fuzzed compilation plans"""
1196+
1197+
parser = ArgumentParser(prog='mx fuzz-jtt-tests', description='Run JTT unit tests with fuzzed phase plans')
1198+
parser.add_argument('--seed', metavar='<seed>', help='Seed to initialize random instance')
1199+
parser.add_argument('--minimal', action='store_true',
1200+
help='Force the use of a minimal fuzzed compilation plan')
1201+
parser.add_argument('--skip-phase-odds', dest='skip_phase_odds', metavar='<int>',
1202+
help='Determine the odds of skipping the insertion of a phase in the fuzzed phase plan')
1203+
parser.add_argument('--high-tier-skip-phase', dest='high_tier_skip_phase', metavar='<int>',
1204+
help='Determine the odds of skipping the insertion of a phase in high tier')
1205+
parser.add_argument('--mid-tier-skip-phase', dest='mid_tier_skip_phase', metavar='<int>',
1206+
help='Determine the odds of skipping the insertion of a phase in mid tier')
1207+
parser.add_argument('--low-tier-skip-phase', dest='low_tier_skip_phase', metavar='<int>',
1208+
help='Determine the odds of skipping the insertion of a phase in low tier')
1209+
1210+
args, parsed_args = parse_split_args(args, parser, "--")
1211+
vm_args = _remove_empty_entries(extraVMarguments) + ['-Dtest.graal.compilationplan.fuzzing=true', '-Dgraal.PrintGraphStateDiff=true']
1212+
1213+
if parsed_args.seed:
1214+
vm_args.append('-Dtest.graal.compilationplan.fuzzing.seed=' + parsed_args.seed)
1215+
if parsed_args.minimal:
1216+
vm_args.append('-Dtest.graal.compilationplan.fuzzing.minimal=true')
1217+
if parsed_args.skip_phase_odds:
1218+
vm_args.append('-Dtest.graal.skip.phase.insertion.odds=' + parsed_args.skip_phase_odds)
1219+
if parsed_args.high_tier_skip_phase:
1220+
vm_args.append('-Dtest.graal.skip.phase.insertion.odds.high.tier=' + parsed_args.high_tier_skip_phase)
1221+
if parsed_args.mid_tier_skip_phase:
1222+
vm_args.append('-Dtest.graal.skip.phase.insertion.odds.mid.tier=' + parsed_args.mid_tier_skip_phase)
1223+
if parsed_args.low_tier_skip_phase:
1224+
vm_args.append('-Dtest.graal.skip.phase.insertion.odds.low.tier=' + parsed_args.low_tier_skip_phase)
1225+
1226+
target_tests = []
1227+
for arg in args:
1228+
if not arg.startswith('-'):
1229+
target_tests.append(arg)
1230+
args.remove(arg)
1231+
if not target_tests:
1232+
target_tests = ['org.graalvm.compiler.jtt.']
1233+
1234+
for test in target_tests:
1235+
UnitTestRun("Fuzz phase plan for tests matching substring " + test, [], tags=GraalTags.unittest + GraalTags.fuzz_jtt_tests).\
1236+
run(['compiler'], [], ['-XX:-UseJVMCICompiler'] + vm_args, _remove_empty_entries(extraUnitTestArguments) + args + [test])
1237+
1238+
11831239
def create_archive(srcdir, arcpath, prefix):
11841240
"""
11851241
Creates a compressed archive of a given directory.
@@ -1338,6 +1394,7 @@ def print_graaljdk_config(args):
13381394
'makegraaljdk': [makegraaljdk_cli, '[options]'],
13391395
'graaljdk-home': [print_graaljdk_home, '[options]'],
13401396
'graaljdk-show': [print_graaljdk_config, '[options]'],
1397+
'fuzz-jtt-tests': [fuzz_jtt_tests, "Runs JTT's unit tests with fuzzed phase plans."],
13411398
})
13421399

13431400
def mx_post_parse_cmd_line(opts):

0 commit comments

Comments
 (0)