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

Commit

Permalink
[dead2] Add basic reductions
Browse files Browse the repository at this point in the history
  • Loading branch information
thetheodor committed Jan 18, 2023
1 parent 4e83bc4 commit af0c501
Show file tree
Hide file tree
Showing 5 changed files with 284 additions and 25 deletions.
24 changes: 21 additions & 3 deletions dead/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from dead.config import dump_config, interactive_init
from dead.differential_testing import DifferentialTestingMode, generate_and_test
from dead.output import write_cases_to_directory
from dead.reduction import reduce_case


def __arg_to_compiler_exe(arg: str | None) -> CompilerExe | None:
Expand Down Expand Up @@ -84,7 +85,6 @@ def parse_args() -> argparse.Namespace:
action=argparse.BooleanOptionalAction,
help="Make the temporary configuration overrides permanent.",
)

parser.add_argument(
"compilation_command1",
type=str,
Expand All @@ -107,6 +107,12 @@ def parse_args() -> argparse.Namespace:
"first command eliminated, in the bidirectional mode (default) a case is "
"interesting as long as at least one command misses a marker",
)

parser.add_argument(
"--reduce",
action=argparse.BooleanOptionalAction,
help="Also reduce the discovered cases",
)
parser.add_argument(
"--jobs",
"-j",
Expand Down Expand Up @@ -148,7 +154,19 @@ def run_as_module() -> None:
setting1,
setting2,
__arg_to_testing_mode(args.testing_mode),
args.number_attempts,
args.number_candidates,
args.jobs,
)
write_cases_to_directory(cases, args.output_directory)
reductions = {}
if args.reduce:
for case in cases:
# XXX: how to a select marker?
target_marker = (
case.markers_only_eliminated_by_setting1
+ case.markers_only_eliminated_by_setting2
)[0]
reduction = reduce_case(case, target_marker, args.jobs)
assert reduction
reductions[case] = reduction

write_cases_to_directory(cases, reductions, args.output_directory)
53 changes: 37 additions & 16 deletions dead/differential_testing.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,24 +51,33 @@ class DifferentialTestingCase:
markers_only_eliminated_by_setting1: tuple[DCEMarker | VRMarker, ...]
markers_only_eliminated_by_setting2: tuple[DCEMarker | VRMarker, ...]

def __post_init__(self) -> None:
assert set(self.markers_only_eliminated_by_setting1).isdisjoint(
self.markers_only_eliminated_by_setting2
)


class DifferentialTestingMode(Enum):
"""
- Unidirectional: a marker is interesting only if the first
compilation setting missed it and the second eliminated
- Bidirectional: a marker is interesting if any of the compilation settings
missed it and the other found it
- Unidirectional: any marker is interesting only if the first
compilation setting missed it and the second eliminated it
- Bidirectional: any marker is interesting if any of the compilation
settings missed it and the other eliminated it
- MarkerMissedByFirst: a particular marker is interesting if the
first compilation setting missed it and the other eliminated it
"""

Unidirectional = 0
Unidirectional = 0 # AnyMissedByFirst?
Bidirectional = 1
MarkerMissedByFirst = 2


def differential_test(
program: SourceProgram,
setting1: CompilationSetting,
setting2: CompilationSetting,
testing_mode: DifferentialTestingMode = DifferentialTestingMode.Bidirectional,
missed_marker: DCEMarker | VRMarker | None = None,
) -> DifferentialTestingCase | None:
"""Instrument `program`, compile it with `setting1` and `setting2` and
check if the set of eliminated markers differ.
Expand All @@ -87,11 +96,16 @@ def differential_test(
setting2 (CompilationSetting):
the second compilation setting with which to
compile the instrumented program
testing_direction (DifferentialTestingDirection):
testing_mode (DifferentialTestingMode):
whether to accept cases whether where any of the two settings miss
at least one marker (Bidirectional), or cases where markers are
eliminated by `setting1` and eliminated by `setting2`
missed by `setting1` and eliminated by `setting2` (Unidirectional).
In MarkerMissedByFirst mode, if `missed_marker` is not
missed by the First setting and eliminated by the other,
the case is not interesting and None is returned.
missed_marker (DCEMarker | VRMarker | None):
If `testing_mode` is MarkerMissecByFirst, only `missed_marker` is
checked: it must be missed by the first setting and found by the other.
Returns:
(DifferentialTestingCase | None):
interesting case if found
Expand All @@ -103,7 +117,9 @@ def differential_test(

# Instrument program
try:
instr_program = instrument_program(program)
instr_program = instrument_program(
setting1.preprocess_program(program, make_compiler_agnostic=True)
)
except AssertionError:
return None

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

# Is the candidate interesting?
if not only_eliminated_by_setting1 and not only_eliminated_by_setting2:
return None
if testing_mode == DifferentialTestingMode.Unidirectional:
if not only_eliminated_by_setting1:
return None
else:
assert testing_mode == DifferentialTestingMode.Bidirectional
match testing_mode:
case DifferentialTestingMode.Bidirectional:
if not only_eliminated_by_setting1 and not only_eliminated_by_setting2:
return None
case DifferentialTestingMode.Unidirectional:
if not only_eliminated_by_setting1:
return None
case DifferentialTestingMode.MarkerMissedByFirst:
assert missed_marker
if missed_marker not in only_eliminated_by_setting2:
return None

return DifferentialTestingCase(
program=instr_program,
Expand All @@ -131,6 +151,7 @@ def differential_test(
)


# XXX: does this really belong in this module?
def generate_and_test(
setting1: CompilationSetting,
setting2: CompilationSetting,
Expand Down
55 changes: 52 additions & 3 deletions dead/output.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,51 @@
from diopter.compiler import CompilationOutput, CompilationOutputKind

from dead.differential_testing import DifferentialTestingCase
from dead.reduction import Reduction


def write_reduction_to_directory(
reduction: Reduction | None, output_directory: Path
) -> None:
if not reduction:
return
reduction_dir = output_directory / "reduction"
reduction_dir.mkdir()
code_file = (reduction_dir / "reduced_code").with_suffix(
reduction.reduced_program.language.to_suffix()
)

with open(code_file, "w") as f:
print(reduction.reduced_program.code, file=f)

with open(reduction_dir / "good_setting", "w") as f:
print(
" ".join(
reduction.good_setting.get_compilation_cmd(
(reduction.reduced_program, Path(code_file.name)),
CompilationOutput(Path("dummy1.s"), CompilationOutputKind.Assembly),
)
),
file=f,
)

with open(reduction_dir / "bad_setting", "w") as f:
print(
" ".join(
reduction.bad_setting.get_compilation_cmd(
(reduction.reduced_program, Path(code_file.name)),
CompilationOutput(Path("dummy1.s"), CompilationOutputKind.Assembly),
)
),
file=f,
)

with open(reduction_dir / "target_marker", "w") as f:
print(reduction.target_marker.to_macro(), file=f)


def write_case_to_directory(
case: DifferentialTestingCase, output_directory: Path
case: DifferentialTestingCase, reduction: Reduction | None, output_directory: Path
) -> None:
output_directory.mkdir(parents=True, exist_ok=True)
code_file = (output_directory / "code").with_suffix(
Expand Down Expand Up @@ -37,6 +78,7 @@ def write_case_to_directory(
),
file=f,
)

with open(output_directory / "markers_only_eliminated_by_setting1", "w") as f:
print(
"\n".join(
Expand All @@ -52,14 +94,21 @@ def write_case_to_directory(
),
file=f,
)
write_reduction_to_directory(reduction, output_directory)


def write_cases_to_directory(
cases: Sequence[DifferentialTestingCase], output_directory: Path
cases: Sequence[DifferentialTestingCase],
reductions: dict[DifferentialTestingCase, Reduction],
output_directory: Path,
) -> None:
output_directory.mkdir(parents=True, exist_ok=True)
output_sub_dir_n = 0
for case in cases:
while (output_directory / str(output_sub_dir_n)).exists():
output_sub_dir_n += 1
write_case_to_directory(case, output_directory / str(output_sub_dir_n))
write_case_to_directory(
case,
reductions[case] if case in reductions else None,
output_directory / str(output_sub_dir_n),
)
Loading

0 comments on commit af0c501

Please sign in to comment.