Skip to content

Commit 7528348

Browse files
committed
wip
1 parent 469f334 commit 7528348

3 files changed

Lines changed: 218 additions & 30 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+
SourceProgram,
15+
SourceFile,
16+
)
17+
from program_markers.iprogram import InstrumentedProgram, InstrumentedFile
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: 140 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
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
68
from typing import Any, Sequence
9+
from pathlib import Path
710

811
from diopter.compiler import (
12+
Source,
913
ASMCompilationOutput,
1014
CompilationOutputType,
1115
CompilationResult,
1216
CompilationSetting,
1317
ExeCompilationOutput,
1418
Language,
1519
SourceProgram,
20+
SourceFile,
1621
)
1722
from program_markers.markers import (
1823
AbortEmitter,
@@ -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,14 @@ 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.
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+
)
173180

174-
Returns:
175-
str:
176-
the source code including the necessary preprocessor directives
177-
"""
178181

179-
return self.generate_preprocessor_directives() + "\n" + self.code
180182

181183
def find_non_eliminated_markers(
182184
self, compilation_setting: CompilationSetting
@@ -197,7 +199,7 @@ def find_non_eliminated_markers(
197199
The non_eliminated markers for the given compilation setting.
198200
"""
199201
asm = compilation_setting.compile_program(
200-
self, ASMCompilationOutput()
202+
self.self_as_source(), ASMCompilationOutput()
201203
).output.read()
202204
non_eliminated_markers = find_non_eliminated_markers_impl(
203205
asm, self.enabled_markers(), self.marker_strategy
@@ -228,6 +230,32 @@ def find_eliminated_markers(
228230
eliminated_markers = eliminated_markers & set(self.enabled_markers())
229231
return tuple(eliminated_markers)
230232

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

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