| 39 | |
| 40 | @dataclasses.dataclass |
| 41 | class _Target(typing.Generic[_S, _R]): |
| 42 | triple: str |
| 43 | condition: str |
| 44 | _: dataclasses.KW_ONLY |
| 45 | args: typing.Sequence[str] = () |
| 46 | optimizer: type[_optimizers.Optimizer] = _optimizers.Optimizer |
| 47 | label_prefix: typing.ClassVar[str] |
| 48 | symbol_prefix: typing.ClassVar[str] |
| 49 | re_global: typing.ClassVar[re.Pattern[str]] |
| 50 | stable: bool = False |
| 51 | debug: bool = False |
| 52 | verbose: bool = False |
| 53 | cflags: str = "" |
| 54 | frame_pointers: bool = False |
| 55 | llvm_version: str = _llvm._LLVM_VERSION |
| 56 | known_symbols: dict[str, int] = dataclasses.field(default_factory=dict) |
| 57 | pyconfig_dir: pathlib.Path = pathlib.Path.cwd().resolve() |
| 58 | |
| 59 | def _get_nop(self) -> bytes: |
| 60 | if re.fullmatch(r"aarch64-.*", self.triple): |
| 61 | nop = b"\x1f\x20\x03\xd5" |
| 62 | elif re.fullmatch(r"x86_64-.*|i686.*", self.triple): |
| 63 | nop = b"\x90" |
| 64 | else: |
| 65 | raise ValueError(f"NOP not defined for {self.triple}") |
| 66 | return nop |
| 67 | |
| 68 | def _compute_digest(self) -> str: |
| 69 | hasher = hashlib.sha256() |
| 70 | hasher.update(self.triple.encode()) |
| 71 | hasher.update(self.debug.to_bytes()) |
| 72 | hasher.update(self.cflags.encode()) |
| 73 | # These dependencies are also reflected in _JITSources in regen.targets: |
| 74 | hasher.update(PYTHON_EXECUTOR_CASES_C_H.read_bytes()) |
| 75 | hasher.update((self.pyconfig_dir / "pyconfig.h").read_bytes()) |
| 76 | for dirpath, _, filenames in sorted(os.walk(TOOLS_JIT)): |
| 77 | # Exclude cache files from digest computation to ensure reproducible builds. |
| 78 | if dirpath.endswith("__pycache__"): |
| 79 | continue |
| 80 | for filename in sorted(filenames): |
| 81 | hasher.update(pathlib.Path(dirpath, filename).read_bytes()) |
| 82 | return hasher.hexdigest() |
| 83 | |
| 84 | async def _parse(self, path: pathlib.Path) -> _stencils.StencilGroup: |
| 85 | group = _stencils.StencilGroup() |
| 86 | args = ["--disassemble", "--reloc", f"{path}"] |
| 87 | output = await _llvm.maybe_run( |
| 88 | "llvm-objdump", args, echo=self.verbose, llvm_version=self.llvm_version |
| 89 | ) |
| 90 | if output is not None: |
| 91 | # Make sure that full paths don't leak out (for reproducibility): |
| 92 | long, short = str(path), str(path.name) |
| 93 | group.code.disassembly.extend( |
| 94 | line.expandtabs().strip().replace(long, short) |
| 95 | for line in output.splitlines() |
| 96 | ) |
| 97 | args = [ |
| 98 | "--elf-output-style=JSON", |