Skip to content

Commit df50ee2

Browse files
committed
wip
1 parent 469f334 commit df50ee2

3 files changed

Lines changed: 221 additions & 32 deletions

File tree

python_src/program_markers/instrumenter.py

Lines changed: 65 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,20 @@
11
from __future__ import annotations
22

3+
import json
34
from dataclasses import replace
45
from enum import Enum
56
from functools import cache
67
from itertools import dropwhile, takewhile
78
from pathlib import Path
89

9-
from diopter.compiler import ClangTool, ClangToolMode, CompilerExe, SourceProgram
10-
from program_markers.iprogram import InstrumentedProgram
10+
from diopter.compiler import (
11+
ClangTool,
12+
ClangToolMode,
13+
CompilerExe,
14+
SourceFile,
15+
SourceProgram,
16+
)
17+
from program_markers.iprogram import InstrumentedFile, InstrumentedProgram
1118
from program_markers.markers import (
1219
DCEMarker,
1320
EnableEmitter,
@@ -306,3 +313,59 @@ def get_code_and_markers(mode: str) -> tuple[str, list[Marker]]:
306313
# (filename argument) plus an include file for the directives. It can use
307314
# the temp InstrumentedProgram to serialize everything and read it back in.
308315
# I can also add a debug mode that checks if the parsed is the same as the original.
316+
317+
318+
def instrument_file(
319+
file: SourceFile,
320+
output: Path,
321+
ignore_functions_with_macros: bool = False,
322+
mode: InstrumenterMode = InstrumenterMode.DCE,
323+
instrumenter: ClangTool | None = None,
324+
clang: CompilerExe | None = None,
325+
timeout: int | None = None,
326+
) -> InstrumentedFile:
327+
# TODO: operate directly on a SourceFile
328+
program = SourceProgram(
329+
language=file.language,
330+
defined_macros=file.defined_macros,
331+
include_paths=file.include_paths + (str(file.filename.parent.resolve()),),
332+
system_include_paths=file.system_include_paths,
333+
flags=file.flags,
334+
code=open(file.filename, "r").read(),
335+
)
336+
iprogram = instrument_program(
337+
program, ignore_functions_with_macros, mode, instrumenter, clang, timeout
338+
)
339+
340+
output = output.resolve()
341+
inc_file = output.with_suffix(".h").with_stem(output.stem + "_program_markers")
342+
with open(output, "w") as f:
343+
f.write(program.code)
344+
345+
with open(inc_file, "w") as f:
346+
f.write(iprogram.generate_preprocessor_directives())
347+
348+
jprogram = iprogram.to_json_dict()
349+
del jprogram["code"]
350+
del jprogram["language"]
351+
del jprogram["include_paths"]
352+
del jprogram["system_include_paths"]
353+
del jprogram["flags"]
354+
355+
j_file = output.with_suffix(".json")
356+
with open(j_file, "w") as f:
357+
json.dump(jprogram, f)
358+
359+
return InstrumentedFile(
360+
marker_strategy=iprogram.marker_strategy,
361+
markers=iprogram.markers,
362+
directive_emitters=iprogram.directive_emitters,
363+
directives_include_file=inc_file,
364+
directives_json_file=j_file,
365+
filename=output,
366+
language=program.language,
367+
defined_macros=program.defined_macros,
368+
include_paths=program.include_paths,
369+
system_include_paths=program.system_include_paths,
370+
flags=program.flags + (f"-include {str(inc_file)}",),
371+
)

python_src/program_markers/iprogram.py

Lines changed: 143 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
from __future__ import annotations
22

3+
import json
34
import re
5+
from abc import ABC, abstractmethod
46
from collections import defaultdict
57
from dataclasses import dataclass, replace
8+
from pathlib import Path
69
from typing import Any, Sequence
710

811
from diopter.compiler import (
@@ -12,6 +15,8 @@
1215
CompilationSetting,
1316
ExeCompilationOutput,
1417
Language,
18+
Source,
19+
SourceFile,
1520
SourceProgram,
1621
)
1722
from program_markers.markers import (
@@ -113,11 +118,15 @@ def collect_markers(
113118

114119

115120
@dataclass(frozen=True, kw_only=True)
116-
class InstrumentedProgram(SourceProgram):
121+
class InstrumentedSourceMixin(ABC):
117122
marker_strategy: MarkerDetectionStrategy
118123
markers: tuple[Marker, ...]
119124
directive_emitters: dict[Marker, MarkerDirectiveEmitter]
120125

126+
@abstractmethod
127+
def self_as_source(self) -> Source:
128+
raise NotImplementedError
129+
121130
def __post_init__(self) -> None:
122131
# All markers ids are unique
123132
marker_ids = set()
@@ -162,21 +171,12 @@ def generate_preprocessor_directives(self) -> str:
162171
for marker, emitter in self.directive_emitters.items()
163172
)
164173

165-
def get_modified_code(self) -> str:
166-
"""Returns the necessary preprocessor directives for markers + self.code.
167-
168-
Only directives for the enabled, disabled and made unreachable markers
169-
are added.
170-
171-
If any markers have not been enabled, disabled, or made unreachable,
172-
then the code not compilable but it can be preprocessed.
173-
174-
Returns:
175-
str:
176-
the source code including the necessary preprocessor directives
177-
"""
178-
179-
return self.generate_preprocessor_directives() + "\n" + self.code
174+
def process_tracking_reachable_markers_output(
175+
self, output: str
176+
) -> tuple[Marker, ...]:
177+
return tuple(
178+
marker for marker in self.enabled_markers() if marker.name in output
179+
)
180180

181181
def find_non_eliminated_markers(
182182
self, compilation_setting: CompilationSetting
@@ -197,7 +197,7 @@ def find_non_eliminated_markers(
197197
The non_eliminated markers for the given compilation setting.
198198
"""
199199
asm = compilation_setting.compile_program(
200-
self, ASMCompilationOutput()
200+
self.self_as_source(), ASMCompilationOutput()
201201
).output.read()
202202
non_eliminated_markers = find_non_eliminated_markers_impl(
203203
asm, self.enabled_markers(), self.marker_strategy
@@ -228,6 +228,32 @@ def find_eliminated_markers(
228228
eliminated_markers = eliminated_markers & set(self.enabled_markers())
229229
return tuple(eliminated_markers)
230230

231+
232+
@dataclass(frozen=True, kw_only=True)
233+
class InstrumentedProgram(SourceProgram, InstrumentedSourceMixin):
234+
marker_strategy: MarkerDetectionStrategy
235+
markers: tuple[Marker, ...]
236+
directive_emitters: dict[Marker, MarkerDirectiveEmitter]
237+
238+
def self_as_source(self) -> SourceProgram:
239+
return self
240+
241+
def get_modified_code(self) -> str:
242+
"""Returns the necessary preprocessor directives for markers + self.code.
243+
244+
Only directives for the enabled, disabled and made unreachable markers
245+
are added.
246+
247+
If any markers have not been enabled, disabled, or made unreachable,
248+
then the code not compilable but it can be preprocessed.
249+
250+
Returns:
251+
str:
252+
the source code including the necessary preprocessor directives
253+
"""
254+
255+
return self.generate_preprocessor_directives() + "\n" + self.code
256+
231257
def replace_markers(self, new_markers: tuple[Marker, ...]) -> InstrumentedProgram:
232258
"""Replaces the markers in the program with the new ones.
233259
Each original marker whose id matches one of the new ones is replaced,
@@ -275,13 +301,6 @@ def compile_program_for_tracking(
275301
tracked_program = replace(self, directive_emitters=new_emitters)
276302
return setting.compile_program(tracked_program, output, timeout=timeout)
277303

278-
def process_tracking_reachable_markers_output(
279-
self, output: str
280-
) -> tuple[Marker, ...]:
281-
return tuple(
282-
marker for marker in self.enabled_markers() if marker.name in output
283-
)
284-
285304
def track_reachable_markers(
286305
self,
287306
args: tuple[str, ...],
@@ -667,3 +686,103 @@ def from_json_dict_impl(
667686
markers=markers,
668687
directive_emitters=directive_emitters,
669688
)
689+
690+
691+
@dataclass(frozen=True, kw_only=True)
692+
class InstrumentedFile(SourceFile, InstrumentedSourceMixin):
693+
marker_strategy: MarkerDetectionStrategy
694+
markers: tuple[Marker, ...]
695+
directive_emitters: dict[Marker, MarkerDirectiveEmitter]
696+
directives_include_file: Path # this should be included
697+
directives_json_file: Path
698+
debug: bool = True
699+
700+
def self_as_source(self) -> SourceFile:
701+
return self
702+
703+
def __post_init__(self) -> None:
704+
assert self.directives_include_file.is_absolute()
705+
assert self.directives_json_file.is_absolute()
706+
assert self.directives_include_file.exists()
707+
assert self.directives_json_file.exists()
708+
assert f"-include {str(self.directives_include_file)}" in self.flags, self.flags
709+
710+
if self.debug:
711+
with open(self.directives_json_file) as f:
712+
j = json.load(f)
713+
j["strategy"] = self.marker_strategy.to_json_dict()
714+
directive_emitters = {
715+
Marker.from_json_dict(m): MarkerDirectiveEmitter.from_json_dict(
716+
directive
717+
)
718+
for m, directive in j["directive_emitters"]
719+
}
720+
assert directive_emitters == self.directive_emitters, (
721+
directive_emitters,
722+
self.directive_emitters,
723+
)
724+
725+
markers = tuple(Marker.from_json_dict(m) for m in j["markers"])
726+
assert markers == self.markers
727+
with open(self.directives_include_file) as f:
728+
assert f.read() == self.generate_preprocessor_directives()
729+
730+
def generate_tracking(self) -> InstrumentedFile:
731+
new_emitters = self.directive_emitters.copy()
732+
tracked_markers = self.enabled_markers()
733+
te = TrackingEmitter()
734+
for marker in tracked_markers:
735+
new_emitters[marker] = te
736+
737+
new_stem = self.directives_include_file.stem + "_tracking"
738+
inc_file = self.directives_include_file.with_stem(new_stem)
739+
with open(inc_file, "w") as f:
740+
f.write(
741+
"\n".join(
742+
emitter.emit_directive(marker)
743+
for marker, emitter in new_emitters.items()
744+
)
745+
)
746+
747+
json_file = self.directives_json_file.with_stem(new_stem)
748+
749+
with open(json_file, "w") as f:
750+
json.dump(
751+
{
752+
"marker_strategy": self.marker_strategy.to_json_dict(),
753+
"directive_emitters": [
754+
(m.to_json_dict(), e.to_json_dict())
755+
for m, e in new_emitters.items()
756+
],
757+
"markers": [m.to_json_dict() for m in self.markers],
758+
},
759+
f,
760+
)
761+
762+
flags = tuple(
763+
f
764+
for f in self.flags
765+
if f != f"-include {str(self.directives_include_file)}"
766+
) + (f"-include {str(inc_file)}",)
767+
768+
return replace(
769+
self,
770+
directive_emitters=new_emitters,
771+
flags=flags,
772+
directives_include_file=inc_file,
773+
directives_json_file=json_file,
774+
)
775+
776+
def generate_tracking_for_refinement_version(self) -> InstrumentedFile:
777+
pass
778+
779+
def refine_markers(self, markers: tuple[Marker, ...]) -> InstrumentedFile:
780+
pass
781+
782+
def make_markers_unreachable(self, markers: tuple[Marker, ...]) -> InstrumentedFile:
783+
pass
784+
785+
def disable_remaining_markers(
786+
self, do_not_disable: Sequence[Marker] = tuple()
787+
) -> InstrumentedFile:
788+
pass

python_src/program_markers/markers.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ def from_json_dict(j: dict[str, Any]) -> VRMarker:
272272
MarkerTypes = (DCEMarker, VRMarker)
273273

274274

275+
@dataclass
275276
class MarkerDirectiveEmitter(ABC):
276277
def emit_directive(self, marker: Marker) -> str:
277278
raise NotImplementedError
@@ -316,19 +317,20 @@ def to_json_dict(self) -> dict[str, Any]:
316317
return j
317318

318319

320+
@dataclass
319321
class NoEmitter(MarkerDirectiveEmitter):
320322
def emit_directive(self, marker: Marker) -> str:
321323
return ""
322324

323325

326+
@dataclass
324327
class EnableEmitter(MarkerDirectiveEmitter):
325-
def __init__(self, strategy: MarkerDetectionStrategy):
326-
self.strategy = strategy
328+
strategy: MarkerDetectionStrategy
327329

328-
def __eq__(self, other: object) -> bool:
329-
if not isinstance(other, MarkerDirectiveEmitter):
330-
return NotImplemented
331-
return isinstance(other, EnableEmitter) and self.strategy == other.strategy
330+
# def __eq__(self, other: object) -> bool:
331+
# if not isinstance(other, MarkerDirectiveEmitter):
332+
# return NotImplemented
333+
# return isinstance(other, EnableEmitter) and self.strategy == other.strategy
332334

333335
def emit_directive(self, marker: Marker) -> str:
334336
return f"""{self.strategy.definitions_and_declarations(marker)}
@@ -339,18 +341,21 @@ def emit_directive(self, marker: Marker) -> str:
339341
"""
340342

341343

344+
@dataclass
342345
class DisableEmitter(MarkerDirectiveEmitter):
343346
def emit_directive(self, marker: Marker) -> str:
344347
return f"#define {marker.macro()} ;"
345348

346349

350+
@dataclass
347351
class UnreachableEmitter(MarkerDirectiveEmitter):
348352
def emit_directive(self, marker: Marker) -> str:
349353
return f"""#define {marker.macro()} \
350354
{marker.marker_statement_prefix()}__builtin_unreachable();{marker.marker_statement_postfix()}
351355
"""
352356

353357

358+
@dataclass
354359
class AbortEmitter(MarkerDirectiveEmitter):
355360
def emit_directive(self, marker: Marker) -> str:
356361
return f"""#define {marker.macro()} \
@@ -361,6 +366,7 @@ def emit_directive(self, marker: Marker) -> str:
361366
"""
362367

363368

369+
@dataclass
364370
class TrackingEmitter(MarkerDirectiveEmitter):
365371
def emit_directive(self, marker: Marker) -> str:
366372
return f""" int {marker.name}_ENCOUNTERED = 0;
@@ -377,6 +383,7 @@ def emit_directive(self, marker: Marker) -> str:
377383
"""
378384

379385

386+
@dataclass
380387
class TrackingForRefinementEmitter(MarkerDirectiveEmitter):
381388
def emit_directive(self, marker: Marker) -> str:
382389
match marker:

0 commit comments

Comments
 (0)