simdref 0.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- simdref/__init__.py +6 -0
- simdref/__main__.py +6 -0
- simdref/annotate.py +448 -0
- simdref/arm_instructions.py +417 -0
- simdref/cli.py +1598 -0
- simdref/display.py +963 -0
- simdref/filters.py +318 -0
- simdref/ingest.py +113 -0
- simdref/ingest_catalog.py +1172 -0
- simdref/ingest_pdf.py +188 -0
- simdref/ingest_sources.py +580 -0
- simdref/lsp.py +208 -0
- simdref/manpages.py +139 -0
- simdref/models.py +225 -0
- simdref/pdfparse/__init__.py +13 -0
- simdref/pdfparse/base.py +116 -0
- simdref/pdfparse/intel.py +614 -0
- simdref/pdfparse/registry.py +19 -0
- simdref/pdfparse/types.py +77 -0
- simdref/pdfrefs.py +95 -0
- simdref/perf.py +220 -0
- simdref/perf_sources/__init__.py +51 -0
- simdref/perf_sources/cores.py +101 -0
- simdref/perf_sources/llvm_mca.py +176 -0
- simdref/perf_sources/llvm_scheduling.py +625 -0
- simdref/perf_sources/merge.py +121 -0
- simdref/queries.py +207 -0
- simdref/riscv.py +446 -0
- simdref/search.py +288 -0
- simdref/storage.py +504 -0
- simdref/templates/__init__.py +0 -0
- simdref/templates/app.js +1590 -0
- simdref/templates/favicon.svg +5 -0
- simdref/templates/index.html +112 -0
- simdref/templates/logo.svg +12 -0
- simdref/templates/style.css +680 -0
- simdref/tui.py +2366 -0
- simdref/web.py +403 -0
- simdref-0.0.0.dist-info/METADATA +240 -0
- simdref-0.0.0.dist-info/RECORD +44 -0
- simdref-0.0.0.dist-info/WHEEL +5 -0
- simdref-0.0.0.dist-info/entry_points.txt +4 -0
- simdref-0.0.0.dist-info/licenses/LICENSE +674 -0
- simdref-0.0.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""Drive the llvm-exegesis → llvm-mc → llvm-mca pipeline per core.
|
|
2
|
+
|
|
3
|
+
The ingester no longer synthesises per-mnemonic asm snippets. It
|
|
4
|
+
enumerates every LLVM-schedulable opcode per target ``(triple, cpu)``
|
|
5
|
+
via ``llvm-exegesis``, disassembles the captured bytes, and feeds the
|
|
6
|
+
result through ``llvm-mca --instruction-tables=full --json``. Three
|
|
7
|
+
subprocess calls per core replace the ~55,000 calls of the former
|
|
8
|
+
regex-based synthesiser.
|
|
9
|
+
|
|
10
|
+
Failure modes:
|
|
11
|
+
|
|
12
|
+
- ``llvm-mca`` / ``llvm-exegesis`` / ``llvm-mc`` absent or too old:
|
|
13
|
+
:class:`LLVMMcaUnavailable`, which the CLI surfaces with an install
|
|
14
|
+
hint.
|
|
15
|
+
- any subprocess emits non-zero status or unparseable output:
|
|
16
|
+
:class:`LLVMMcaError` with the offending core's triple + cpu.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import shutil
|
|
22
|
+
import subprocess
|
|
23
|
+
from dataclasses import dataclass
|
|
24
|
+
from typing import Any, Iterable
|
|
25
|
+
|
|
26
|
+
from simdref.models import InstructionRecord
|
|
27
|
+
from simdref.perf_sources.cores import CANONICAL_CORES, CoreSpec
|
|
28
|
+
from simdref.perf_sources.merge import PerfRow
|
|
29
|
+
|
|
30
|
+
LLVM_MCA_MIN_VERSION: int = 18 # JSON output stabilised in LLVM 18.
|
|
31
|
+
LLVM_MCA_CITATION = "https://llvm.org/docs/CommandGuide/llvm-mca.html"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class LLVMMcaUnavailable(RuntimeError):
|
|
35
|
+
"""Raised when a required LLVM binary is missing or too old."""
|
|
36
|
+
|
|
37
|
+
install_hint: str = (
|
|
38
|
+
"Install the LLVM toolchain (llvm-mca, llvm-exegesis, llvm-mc) to "
|
|
39
|
+
"build the modeled perf catalog:\n"
|
|
40
|
+
" apt: sudo apt install llvm\n"
|
|
41
|
+
" brew: brew install llvm\n"
|
|
42
|
+
" conda: conda install -c conda-forge llvm-tools\n"
|
|
43
|
+
"Or download the pre-built release artifact: simdref update"
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class LLVMMcaError(RuntimeError):
|
|
48
|
+
"""Raised when one of the pipeline stages emits unusable output."""
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(frozen=True)
|
|
52
|
+
class LLVMMcaVersion:
|
|
53
|
+
major: int
|
|
54
|
+
raw: str
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def detect_llvm_mca_version(executable: str = "llvm-mca") -> LLVMMcaVersion:
|
|
58
|
+
"""Return the LLVM major version, or raise :class:`LLVMMcaUnavailable`."""
|
|
59
|
+
if shutil.which(executable) is None:
|
|
60
|
+
raise LLVMMcaUnavailable(f"{executable!r} not found on PATH")
|
|
61
|
+
try:
|
|
62
|
+
proc = subprocess.run(
|
|
63
|
+
[executable, "--version"],
|
|
64
|
+
check=True,
|
|
65
|
+
capture_output=True,
|
|
66
|
+
text=True,
|
|
67
|
+
timeout=10,
|
|
68
|
+
)
|
|
69
|
+
except (subprocess.SubprocessError, OSError) as exc:
|
|
70
|
+
raise LLVMMcaUnavailable(f"failed to run {executable} --version: {exc}") from exc
|
|
71
|
+
for line in (proc.stdout + proc.stderr).splitlines():
|
|
72
|
+
token = line.lower()
|
|
73
|
+
if "llvm version" in token:
|
|
74
|
+
tail = token.split("llvm version", 1)[1].strip().split()[0]
|
|
75
|
+
head = tail.split(".", 1)[0]
|
|
76
|
+
if head.isdigit():
|
|
77
|
+
major = int(head)
|
|
78
|
+
if major < LLVM_MCA_MIN_VERSION:
|
|
79
|
+
raise LLVMMcaUnavailable(
|
|
80
|
+
f"llvm-mca {major} is older than required {LLVM_MCA_MIN_VERSION}"
|
|
81
|
+
)
|
|
82
|
+
return LLVMMcaVersion(major=major, raw=tail)
|
|
83
|
+
raise LLVMMcaUnavailable(f"could not parse version from {executable} --version")
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def parse_llvm_mca_json(
|
|
87
|
+
payload: dict[str, Any],
|
|
88
|
+
*,
|
|
89
|
+
core: CoreSpec,
|
|
90
|
+
mnemonic: str,
|
|
91
|
+
mca_version: str,
|
|
92
|
+
) -> PerfRow | None:
|
|
93
|
+
"""Extract one :class:`PerfRow` from a single-region ``llvm-mca --json`` payload.
|
|
94
|
+
|
|
95
|
+
Kept as a small utility for callers that want to run llvm-mca on a
|
|
96
|
+
standalone snippet (outside the scheduling pipeline). It tolerates
|
|
97
|
+
both the modern ``InstructionInfoView`` schema (LLVM 18+) and the
|
|
98
|
+
legacy flat ``Instructions`` + ``SummaryView.IPC`` schema used by
|
|
99
|
+
older fixtures.
|
|
100
|
+
"""
|
|
101
|
+
regions = payload.get("CodeRegions") or []
|
|
102
|
+
if not regions:
|
|
103
|
+
return None
|
|
104
|
+
region = regions[0]
|
|
105
|
+
info_view = region.get("InstructionInfoView") or {}
|
|
106
|
+
info_list = info_view.get("InstructionList") or []
|
|
107
|
+
latency: Any = None
|
|
108
|
+
rthroughput: Any = None
|
|
109
|
+
if info_list and isinstance(info_list[0], dict):
|
|
110
|
+
latency = info_list[0].get("Latency")
|
|
111
|
+
rthroughput = info_list[0].get("RThroughput")
|
|
112
|
+
if latency is None and rthroughput is None:
|
|
113
|
+
insts = region.get("Instructions") or []
|
|
114
|
+
if insts and isinstance(insts[0], dict):
|
|
115
|
+
latency = insts[0].get("Latency")
|
|
116
|
+
summary = region.get("SummaryView") or {}
|
|
117
|
+
ipc = summary.get("IPC")
|
|
118
|
+
if isinstance(ipc, (int, float)) and ipc > 0:
|
|
119
|
+
rthroughput = 1.0 / float(ipc)
|
|
120
|
+
cpi = ""
|
|
121
|
+
if isinstance(rthroughput, (int, float)):
|
|
122
|
+
cpi = f"{float(rthroughput):.3f}".rstrip("0").rstrip(".")
|
|
123
|
+
return PerfRow(
|
|
124
|
+
mnemonic=mnemonic,
|
|
125
|
+
core=core.canonical_id,
|
|
126
|
+
source="llvm-mca",
|
|
127
|
+
source_kind="modeled",
|
|
128
|
+
source_version=mca_version,
|
|
129
|
+
architecture="arm" if core.architecture == "aarch64" else core.architecture,
|
|
130
|
+
latency=str(latency) if latency is not None else "",
|
|
131
|
+
cpi=cpi,
|
|
132
|
+
applies_to="mnemonic",
|
|
133
|
+
citation_url=LLVM_MCA_CITATION,
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def ingest_llvm_mca(
|
|
138
|
+
records: Iterable[InstructionRecord],
|
|
139
|
+
*,
|
|
140
|
+
cores: Iterable[CoreSpec] | None = None,
|
|
141
|
+
executable: str = "llvm-mca",
|
|
142
|
+
cache_root: Any = None,
|
|
143
|
+
) -> tuple[list[PerfRow], str]:
|
|
144
|
+
"""Enumerate LLVM-schedulable opcodes across cores, returning rows + version.
|
|
145
|
+
|
|
146
|
+
Each aarch64 / riscv core triggers one llvm-exegesis +
|
|
147
|
+
llvm-mc + llvm-mca pipeline, cached under ``vendor/perf-cache/``.
|
|
148
|
+
x86 cores are skipped (scheduling models come from uops.info).
|
|
149
|
+
|
|
150
|
+
*records* is accepted for API symmetry with the previous implementation
|
|
151
|
+
but is no longer consumed — the pipeline reads LLVM's own InstrInfo
|
|
152
|
+
tables rather than the catalog.
|
|
153
|
+
|
|
154
|
+
Raises :class:`LLVMMcaUnavailable` when a required binary is absent
|
|
155
|
+
or too old. Raises :class:`LLVMMcaError` on any pipeline failure —
|
|
156
|
+
no silent fallback.
|
|
157
|
+
"""
|
|
158
|
+
# Lazy import to break the circular dependency (llvm_scheduling
|
|
159
|
+
# imports the error classes + citation URL from this module).
|
|
160
|
+
from simdref.perf_sources import llvm_scheduling # noqa: PLC0415
|
|
161
|
+
|
|
162
|
+
version = detect_llvm_mca_version(executable)
|
|
163
|
+
_ = list(records) # drain iterator; kept for signature compatibility
|
|
164
|
+
target_cores = list(cores) if cores is not None else list(CANONICAL_CORES)
|
|
165
|
+
rows: list[PerfRow] = []
|
|
166
|
+
for core in target_cores:
|
|
167
|
+
if core.architecture == "x86":
|
|
168
|
+
continue
|
|
169
|
+
rows.extend(
|
|
170
|
+
llvm_scheduling.collect_core_schedule(
|
|
171
|
+
core,
|
|
172
|
+
mca_version=version.raw,
|
|
173
|
+
cache_root=cache_root,
|
|
174
|
+
)
|
|
175
|
+
)
|
|
176
|
+
return rows, version.raw
|