Skip to content
This repository was archived by the owner on Nov 19, 2024. It is now read-only.

Commit e595f20

Browse files
committed
[dead2] Add basic reductions
1 parent 4e83bc4 commit e595f20

File tree

5 files changed

+281
-25
lines changed

5 files changed

+281
-25
lines changed

dead/__init__.py

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from dead.config import dump_config, interactive_init
88
from dead.differential_testing import DifferentialTestingMode, generate_and_test
99
from dead.output import write_cases_to_directory
10+
from dead.reduction import reduce_case
1011

1112

1213
def __arg_to_compiler_exe(arg: str | None) -> CompilerExe | None:
@@ -84,7 +85,6 @@ def parse_args() -> argparse.Namespace:
8485
action=argparse.BooleanOptionalAction,
8586
help="Make the temporary configuration overrides permanent.",
8687
)
87-
8888
parser.add_argument(
8989
"compilation_command1",
9090
type=str,
@@ -107,6 +107,12 @@ def parse_args() -> argparse.Namespace:
107107
"first command eliminated, in the bidirectional mode (default) a case is "
108108
"interesting as long as at least one command misses a marker",
109109
)
110+
111+
parser.add_argument(
112+
"--reduce",
113+
action=argparse.BooleanOptionalAction,
114+
help="Also reduce the discovered cases",
115+
)
110116
parser.add_argument(
111117
"--jobs",
112118
"-j",
@@ -148,7 +154,19 @@ def run_as_module() -> None:
148154
setting1,
149155
setting2,
150156
__arg_to_testing_mode(args.testing_mode),
151-
args.number_attempts,
157+
args.number_candidates,
152158
args.jobs,
153159
)
154-
write_cases_to_directory(cases, args.output_directory)
160+
reductions = {}
161+
if args.reduce:
162+
for case in cases:
163+
# XXX: how to a select marker?
164+
target_marker = (
165+
case.markers_only_eliminated_by_setting1
166+
+ case.markers_only_eliminated_by_setting2
167+
)[0]
168+
reduction = reduce_case(case, target_marker, args.jobs)
169+
assert reduction
170+
reductions[case] = reduction
171+
172+
write_cases_to_directory(cases, reductions, args.output_directory)

dead/differential_testing.py

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,24 +51,33 @@ class DifferentialTestingCase:
5151
markers_only_eliminated_by_setting1: tuple[DCEMarker | VRMarker, ...]
5252
markers_only_eliminated_by_setting2: tuple[DCEMarker | VRMarker, ...]
5353

54+
def __post_init__(self) -> None:
55+
assert set(self.markers_only_eliminated_by_setting1).isdisjoint(
56+
self.markers_only_eliminated_by_setting2
57+
)
58+
5459

5560
class DifferentialTestingMode(Enum):
5661
"""
57-
- Unidirectional: a marker is interesting only if the first
58-
compilation setting missed it and the second eliminated
59-
- Bidirectional: a marker is interesting if any of the compilation settings
60-
missed it and the other found it
62+
- Unidirectional: any marker is interesting only if the first
63+
compilation setting missed it and the second eliminated it
64+
- Bidirectional: any marker is interesting if any of the compilation
65+
settings missed it and the other eliminated it
66+
- MarkerMissedByFirst: a particular marker is interesting if the
67+
first compilation setting missed it and the other eliminated it
6168
"""
6269

63-
Unidirectional = 0
70+
Unidirectional = 0 # AnyMissedByFirst?
6471
Bidirectional = 1
72+
MarkerMissedByFirst = 2
6573

6674

6775
def differential_test(
6876
program: SourceProgram,
6977
setting1: CompilationSetting,
7078
setting2: CompilationSetting,
7179
testing_mode: DifferentialTestingMode = DifferentialTestingMode.Bidirectional,
80+
missed_marker: DCEMarker | VRMarker | None = None,
7281
) -> DifferentialTestingCase | None:
7382
"""Instrument `program`, compile it with `setting1` and `setting2` and
7483
check if the set of eliminated markers differ.
@@ -87,11 +96,16 @@ def differential_test(
8796
setting2 (CompilationSetting):
8897
the second compilation setting with which to
8998
compile the instrumented program
90-
testing_direction (DifferentialTestingDirection):
99+
testing_mode (DifferentialTestingMode):
91100
whether to accept cases whether where any of the two settings miss
92101
at least one marker (Bidirectional), or cases where markers are
93-
eliminated by `setting1` and eliminated by `setting2`
94-
102+
missed by `setting1` and eliminated by `setting2` (Unidirectional).
103+
In MarkerMissedByFirst mode, if `missed_marker` is not
104+
missed by the First setting and eliminated by the other,
105+
the case is not interesting and None is returned.
106+
missed_marker (DCEMarker | VRMarker | None):
107+
If `testing_mode` is MarkerMissecByFirst, only `missed_marker` is
108+
checked: it must be missed by the first setting and found by the other.
95109
Returns:
96110
(DifferentialTestingCase | None):
97111
interesting case if found
@@ -103,7 +117,9 @@ def differential_test(
103117

104118
# Instrument program
105119
try:
106-
instr_program = instrument_program(program)
120+
instr_program = instrument_program(
121+
setting1.preprocess_program(program, make_compiler_agnostic=True)
122+
)
107123
except AssertionError:
108124
return None
109125

@@ -114,13 +130,17 @@ def differential_test(
114130
only_eliminated_by_setting2 = tuple(dead_markers2 - dead_markers1)
115131

116132
# Is the candidate interesting?
117-
if not only_eliminated_by_setting1 and not only_eliminated_by_setting2:
118-
return None
119-
if testing_mode == DifferentialTestingMode.Unidirectional:
120-
if not only_eliminated_by_setting1:
121-
return None
122-
else:
123-
assert testing_mode == DifferentialTestingMode.Bidirectional
133+
match testing_mode:
134+
case DifferentialTestingMode.Bidirectional:
135+
if not only_eliminated_by_setting1 and not only_eliminated_by_setting2:
136+
return None
137+
case DifferentialTestingMode.Unidirectional:
138+
if not only_eliminated_by_setting1:
139+
return None
140+
case DifferentialTestingMode.MarkerMissedByFirst:
141+
assert missed_marker
142+
if missed_marker not in only_eliminated_by_setting2:
143+
return None
124144

125145
return DifferentialTestingCase(
126146
program=instr_program,
@@ -131,6 +151,7 @@ def differential_test(
131151
)
132152

133153

154+
# XXX: does this really belong in this module?
134155
def generate_and_test(
135156
setting1: CompilationSetting,
136157
setting2: CompilationSetting,

dead/output.py

Lines changed: 52 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,51 @@
44
from diopter.compiler import CompilationOutput, CompilationOutputKind
55

66
from dead.differential_testing import DifferentialTestingCase
7+
from dead.reduction import Reduction
8+
9+
10+
def write_reduction_to_directory(
11+
reduction: Reduction | None, output_directory: Path
12+
) -> None:
13+
if not reduction:
14+
return
15+
reduction_dir = output_directory / "reduction"
16+
reduction_dir.mkdir()
17+
code_file = (reduction_dir / "reduced_code").with_suffix(
18+
reduction.reduced_program.language.to_suffix()
19+
)
20+
21+
with open(code_file, "w") as f:
22+
print(reduction.reduced_program.code, file=f)
23+
24+
with open(reduction_dir / "good_setting", "w") as f:
25+
print(
26+
" ".join(
27+
reduction.good_setting.get_compilation_cmd(
28+
(reduction.reduced_program, Path(code_file.name)),
29+
CompilationOutput(Path("dummy1.s"), CompilationOutputKind.Assembly),
30+
)
31+
),
32+
file=f,
33+
)
34+
35+
with open(reduction_dir / "bad_setting", "w") as f:
36+
print(
37+
" ".join(
38+
reduction.bad_setting.get_compilation_cmd(
39+
(reduction.reduced_program, Path(code_file.name)),
40+
CompilationOutput(Path("dummy1.s"), CompilationOutputKind.Assembly),
41+
)
42+
),
43+
file=f,
44+
)
45+
46+
with open(reduction_dir / "target_marker", "w") as f:
47+
print(reduction.target_marker.to_macro(), file=f)
748

849

950
def write_case_to_directory(
10-
case: DifferentialTestingCase, output_directory: Path
51+
case: DifferentialTestingCase, reduction: Reduction | None, output_directory: Path
1152
) -> None:
1253
output_directory.mkdir(parents=True, exist_ok=True)
1354
code_file = (output_directory / "code").with_suffix(
@@ -37,6 +78,7 @@ def write_case_to_directory(
3778
),
3879
file=f,
3980
)
81+
4082
with open(output_directory / "markers_only_eliminated_by_setting1", "w") as f:
4183
print(
4284
"\n".join(
@@ -52,14 +94,21 @@ def write_case_to_directory(
5294
),
5395
file=f,
5496
)
97+
write_reduction_to_directory(reduction, output_directory)
5598

5699

57100
def write_cases_to_directory(
58-
cases: Sequence[DifferentialTestingCase], output_directory: Path
101+
cases: Sequence[DifferentialTestingCase],
102+
reductions: dict[DifferentialTestingCase, Reduction],
103+
output_directory: Path,
59104
) -> None:
60105
output_directory.mkdir(parents=True, exist_ok=True)
61106
output_sub_dir_n = 0
62107
for case in cases:
63108
while (output_directory / str(output_sub_dir_n)).exists():
64109
output_sub_dir_n += 1
65-
write_case_to_directory(case, output_directory / str(output_sub_dir_n))
110+
write_case_to_directory(
111+
case,
112+
reductions[case] if case in reductions else None,
113+
output_directory / str(output_sub_dir_n),
114+
)

0 commit comments

Comments
 (0)