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
simdref/web.py
ADDED
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
"""Static web-app export for simdref.
|
|
2
|
+
|
|
3
|
+
Generates a self-contained single-page application (``index.html``) plus a
|
|
4
|
+
two-tier data split:
|
|
5
|
+
|
|
6
|
+
* **search-index.json** -- compact search data loaded at startup (~400 KB gzipped).
|
|
7
|
+
* **detail-chunks/{PREFIX}.json** -- full instruction details (operands,
|
|
8
|
+
measurements) loaded on demand when the user selects a result.
|
|
9
|
+
|
|
10
|
+
The HTML template is assembled from three source files under
|
|
11
|
+
``simdref/templates/`` (HTML shell, CSS, JS) and inlined into a single
|
|
12
|
+
``index.html``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import gzip
|
|
18
|
+
import json
|
|
19
|
+
import re
|
|
20
|
+
import shutil
|
|
21
|
+
from collections import defaultdict
|
|
22
|
+
from dataclasses import asdict
|
|
23
|
+
from importlib import resources
|
|
24
|
+
from pathlib import Path
|
|
25
|
+
|
|
26
|
+
from simdref import __version__
|
|
27
|
+
from simdref.filters import FilterSpec, CategorySpec
|
|
28
|
+
from simdref.models import Catalog
|
|
29
|
+
from simdref.display import (
|
|
30
|
+
display_architecture,
|
|
31
|
+
display_isa,
|
|
32
|
+
display_instruction_form,
|
|
33
|
+
isa_families,
|
|
34
|
+
isa_family,
|
|
35
|
+
isa_to_sub_isa,
|
|
36
|
+
strip_instruction_decorators,
|
|
37
|
+
)
|
|
38
|
+
from simdref.perf import best_numeric, latency_cycle_values, variant_perf_summary
|
|
39
|
+
from simdref.pdfrefs import normalize_pdf_refs
|
|
40
|
+
from simdref.storage import derive_arm_arch
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _load_template() -> str:
|
|
44
|
+
"""Assemble the HTML template from shell + CSS + JS source files."""
|
|
45
|
+
tpl = resources.files("simdref.templates")
|
|
46
|
+
html = tpl.joinpath("index.html").read_text()
|
|
47
|
+
css = tpl.joinpath("style.css").read_text()
|
|
48
|
+
js = tpl.joinpath("app.js").read_text()
|
|
49
|
+
return html.replace("/* __CSS__ */", css).replace("/* __JS__ */", js)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _latency_value(latencies: list[dict]) -> str:
|
|
53
|
+
"""Best latency value from a list of latency dicts."""
|
|
54
|
+
return best_numeric(latency_cycle_values(latencies))
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _web_measurements(item) -> list[dict]:
|
|
58
|
+
"""Per-microarchitecture measurement rows for the web UI."""
|
|
59
|
+
rows: list[dict] = []
|
|
60
|
+
for uarch, details in item.arch_details.items():
|
|
61
|
+
measurement = details.get("measurement") or {}
|
|
62
|
+
if not measurement:
|
|
63
|
+
continue
|
|
64
|
+
rows.append({
|
|
65
|
+
"uarch": uarch,
|
|
66
|
+
"ports": measurement.get("ports", "-"),
|
|
67
|
+
"latency": _latency_value(details.get("latencies") or []),
|
|
68
|
+
"tpLoop": measurement.get("TP_loop") or measurement.get("TP") or "-",
|
|
69
|
+
"tpUnrolled": measurement.get("TP_unrolled", "-"),
|
|
70
|
+
"tpPorts": measurement.get("TP_ports", "-"),
|
|
71
|
+
"uops": measurement.get("uops", "-"),
|
|
72
|
+
"sourceKind": details.get("source_kind") or "measured",
|
|
73
|
+
})
|
|
74
|
+
return rows
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _truncate(text: str, max_len: int = 120) -> str:
|
|
78
|
+
if len(text) <= max_len:
|
|
79
|
+
return text
|
|
80
|
+
return text[:max_len - 1] + "\u2026"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _chunk_prefix(mnemonic: str) -> str:
|
|
84
|
+
"""3-char uppercase prefix for grouping instructions into chunks."""
|
|
85
|
+
clean = mnemonic.strip().upper()
|
|
86
|
+
return clean[:3] if len(clean) >= 3 else clean
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
_INTRINSIC_PREFIX_STRIP = re.compile(r"^(?:mm\d*|sv|vq?)_?", re.IGNORECASE)
|
|
90
|
+
_INTRINSIC_ALNUM = re.compile(r"[A-Za-z0-9]")
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
def _intrinsic_chunk_prefix(name: str) -> str:
|
|
94
|
+
"""3-char bucket key for grouping intrinsics by their op name.
|
|
95
|
+
|
|
96
|
+
Strips leading underscores, an optional ``riscv_`` namespace, and a
|
|
97
|
+
single width/family token (``mm``, ``mm256``, ``mm512``, ``sv``,
|
|
98
|
+
``v``, ``vq``) so that buckets cluster by operation (``add``,
|
|
99
|
+
``cvt``, ``fma``) rather than by vector width or platform. Without
|
|
100
|
+
the ``riscv_`` strip the 74k RISC-V vector intrinsics all fall into
|
|
101
|
+
a single 89 MB bucket.
|
|
102
|
+
|
|
103
|
+
Falls back to ``misc`` when a name does not produce at least two
|
|
104
|
+
alphanumeric characters after stripping.
|
|
105
|
+
|
|
106
|
+
The same function is re-implemented in ``app.js`` — keep in sync.
|
|
107
|
+
"""
|
|
108
|
+
s = name.lstrip("_")
|
|
109
|
+
if s[:6].lower() == "riscv_":
|
|
110
|
+
s = s[6:]
|
|
111
|
+
m = _INTRINSIC_PREFIX_STRIP.match(s)
|
|
112
|
+
if m and m.end() < len(s):
|
|
113
|
+
s = s[m.end():]
|
|
114
|
+
s = s.lstrip("_")
|
|
115
|
+
clean = "".join(_INTRINSIC_ALNUM.findall(s)).lower()
|
|
116
|
+
if len(clean) < 2:
|
|
117
|
+
return "misc"
|
|
118
|
+
return clean[:3]
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _intrinsic_search_fields(item) -> list[str]:
|
|
122
|
+
"""Slim search-only field set: only what ranking/scoring actually needs."""
|
|
123
|
+
return [
|
|
124
|
+
item.name,
|
|
125
|
+
item.description or "",
|
|
126
|
+
display_isa(item.isa),
|
|
127
|
+
" ".join(item.instructions or []),
|
|
128
|
+
]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _instruction_search_fields(item) -> list[str]:
|
|
132
|
+
display_key = display_instruction_form(item.key)
|
|
133
|
+
display_form = display_instruction_form(item.form)
|
|
134
|
+
return [
|
|
135
|
+
strip_instruction_decorators(item.mnemonic or ""),
|
|
136
|
+
display_key,
|
|
137
|
+
display_form,
|
|
138
|
+
item.summary or "",
|
|
139
|
+
display_isa(item.isa),
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _filter_spec_for_catalog(catalog: Catalog) -> FilterSpec:
|
|
144
|
+
spec = FilterSpec()
|
|
145
|
+
aggregate: dict[tuple[str, str, str], int] = {}
|
|
146
|
+
for item in catalog.intrinsics:
|
|
147
|
+
if not item.category:
|
|
148
|
+
continue
|
|
149
|
+
families = {isa_family(v) for v in (item.isa or [])} or {"Other"}
|
|
150
|
+
for family in families:
|
|
151
|
+
key = (family, item.category, item.subcategory or "")
|
|
152
|
+
aggregate[key] = aggregate.get(key, 0) + 1
|
|
153
|
+
for item in catalog.instructions:
|
|
154
|
+
cat = (item.metadata or {}).get("category", "") if isinstance(item.metadata, dict) else ""
|
|
155
|
+
if not cat:
|
|
156
|
+
continue
|
|
157
|
+
families = {isa_family(v) for v in (item.isa or [])} or {"Other"}
|
|
158
|
+
for family in families:
|
|
159
|
+
key = (family, cat, "")
|
|
160
|
+
aggregate[key] = aggregate.get(key, 0) + 1
|
|
161
|
+
spec.categories = [
|
|
162
|
+
CategorySpec(family=fam, category=cat, subcategory=sub, count=n)
|
|
163
|
+
for (fam, cat, sub), n in sorted(aggregate.items())
|
|
164
|
+
]
|
|
165
|
+
return spec
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _isa_config() -> dict:
|
|
169
|
+
"""Legacy isa_config block embedded in ``search-index.json``."""
|
|
170
|
+
payload = FilterSpec().to_json()
|
|
171
|
+
payload.pop("categories", None)
|
|
172
|
+
return payload
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _search_payload(catalog: Catalog) -> dict:
|
|
176
|
+
"""Compact search-only payload for fast initial load."""
|
|
177
|
+
# Pre-compute perf summaries for instructions (keyed by instruction key).
|
|
178
|
+
instr_perf: dict[str, tuple[str, str]] = {}
|
|
179
|
+
for item in catalog.instructions:
|
|
180
|
+
lat, cpi = variant_perf_summary(item.arch_details)
|
|
181
|
+
instr_perf[item.db_key] = (lat, cpi)
|
|
182
|
+
|
|
183
|
+
def _intrinsic_perf(item) -> tuple[str, str]:
|
|
184
|
+
"""Best lat/cpi from the primary linked instruction."""
|
|
185
|
+
for inst_ref in item.instruction_refs:
|
|
186
|
+
key = inst_ref.get("key", "")
|
|
187
|
+
if key in instr_perf:
|
|
188
|
+
return instr_perf[key]
|
|
189
|
+
return ("-", "-")
|
|
190
|
+
|
|
191
|
+
def _intrinsic_primary_instr(item) -> str:
|
|
192
|
+
for ref in item.instruction_refs or []:
|
|
193
|
+
key = ref.get("key") if isinstance(ref, dict) else None
|
|
194
|
+
if key:
|
|
195
|
+
return key
|
|
196
|
+
return (item.instructions or [""])[0]
|
|
197
|
+
|
|
198
|
+
intrinsics_out = []
|
|
199
|
+
for item in catalog.intrinsics:
|
|
200
|
+
lat, cpi = _intrinsic_perf(item)
|
|
201
|
+
fields = _intrinsic_search_fields(item)
|
|
202
|
+
entry: dict = {
|
|
203
|
+
"name": item.name,
|
|
204
|
+
"subtitle": _truncate(item.description or "", 80),
|
|
205
|
+
"architecture": item.architecture,
|
|
206
|
+
"isa": item.isa,
|
|
207
|
+
"lat": lat,
|
|
208
|
+
"cpi": cpi,
|
|
209
|
+
"display_architecture": display_architecture(item.architecture),
|
|
210
|
+
"display_isa": display_isa(item.isa),
|
|
211
|
+
"isa_families": isa_families(item.isa),
|
|
212
|
+
"isa_subs": list(dict.fromkeys(filter(None, (isa_to_sub_isa(value) for value in item.isa)))),
|
|
213
|
+
"search_fields": fields,
|
|
214
|
+
}
|
|
215
|
+
primary = _intrinsic_primary_instr(item)
|
|
216
|
+
if primary:
|
|
217
|
+
entry["primary_instr"] = primary
|
|
218
|
+
arm_arch = derive_arm_arch(item.isa, item.metadata if isinstance(item.metadata, dict) else {})
|
|
219
|
+
if arm_arch:
|
|
220
|
+
entry["arm_arch"] = arm_arch
|
|
221
|
+
category = (item.metadata or {}).get("category", "") if isinstance(item.metadata, dict) else ""
|
|
222
|
+
if category:
|
|
223
|
+
entry["category"] = category
|
|
224
|
+
intrinsics_out.append(entry)
|
|
225
|
+
|
|
226
|
+
instructions_out = []
|
|
227
|
+
for item in catalog.instructions:
|
|
228
|
+
lat, cpi = instr_perf[item.db_key]
|
|
229
|
+
fields = _instruction_search_fields(item)
|
|
230
|
+
instructions_out.append({
|
|
231
|
+
"key": item.db_key,
|
|
232
|
+
"mnemonic": item.mnemonic,
|
|
233
|
+
"form": item.form,
|
|
234
|
+
"architecture": item.architecture,
|
|
235
|
+
"summary": _truncate(item.summary or "", 80),
|
|
236
|
+
"isa": item.isa,
|
|
237
|
+
"linked_intrinsics": item.linked_intrinsics,
|
|
238
|
+
"lat": lat,
|
|
239
|
+
"cpi": cpi,
|
|
240
|
+
"display_architecture": display_architecture(item.architecture),
|
|
241
|
+
"display_key": display_instruction_form(item.key),
|
|
242
|
+
"display_form": display_instruction_form(item.form),
|
|
243
|
+
"display_mnemonic": strip_instruction_decorators(item.mnemonic),
|
|
244
|
+
"display_isa": display_isa(item.isa),
|
|
245
|
+
"isa_families": isa_families(item.isa),
|
|
246
|
+
"isa_subs": list(dict.fromkeys(filter(None, (isa_to_sub_isa(value) for value in item.isa)))),
|
|
247
|
+
"search_fields": fields,
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
return {
|
|
251
|
+
"generated_at": catalog.generated_at,
|
|
252
|
+
"sources": [asdict(source) for source in catalog.sources],
|
|
253
|
+
"isa_config": _isa_config(),
|
|
254
|
+
"intrinsics": intrinsics_out,
|
|
255
|
+
"instructions": instructions_out,
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
def _detail_chunks(catalog: Catalog) -> dict[str, dict]:
|
|
260
|
+
"""Group full instruction details by 3-char mnemonic prefix.
|
|
261
|
+
|
|
262
|
+
Returns a mapping of ``{prefix: {key: detail_dict}}``.
|
|
263
|
+
"""
|
|
264
|
+
chunks: dict[str, dict] = defaultdict(dict)
|
|
265
|
+
for item in catalog.instructions:
|
|
266
|
+
prefix = _chunk_prefix(item.mnemonic)
|
|
267
|
+
chunks[prefix][item.db_key] = {
|
|
268
|
+
"mnemonic": item.mnemonic,
|
|
269
|
+
"form": item.form,
|
|
270
|
+
"architecture": item.architecture,
|
|
271
|
+
"display_architecture": display_architecture(item.architecture),
|
|
272
|
+
"display_form": display_instruction_form(item.form),
|
|
273
|
+
"display_mnemonic": strip_instruction_decorators(item.mnemonic),
|
|
274
|
+
"summary": item.summary,
|
|
275
|
+
"description": item.description,
|
|
276
|
+
"isa": item.isa,
|
|
277
|
+
"display_isa": display_isa(item.isa),
|
|
278
|
+
"operand_details": [
|
|
279
|
+
{
|
|
280
|
+
"idx": op.get("idx", ""),
|
|
281
|
+
"r": op.get("r", ""),
|
|
282
|
+
"w": op.get("w", ""),
|
|
283
|
+
"type": op.get("type", ""),
|
|
284
|
+
"width": op.get("width", ""),
|
|
285
|
+
"xtype": op.get("xtype", ""),
|
|
286
|
+
"name": op.get("name", ""),
|
|
287
|
+
}
|
|
288
|
+
for op in item.operand_details
|
|
289
|
+
],
|
|
290
|
+
"metadata": {
|
|
291
|
+
k: v for k, v in item.metadata.items()
|
|
292
|
+
if k in {"url", "url-ref", "category", "cpl", "intel-sdm-url", "intel-sdm-page-start", "intel-sdm-page-end"}
|
|
293
|
+
},
|
|
294
|
+
"pdf_refs": normalize_pdf_refs(item.pdf_refs, item.metadata),
|
|
295
|
+
"linked_intrinsics": item.linked_intrinsics,
|
|
296
|
+
"measurements": _web_measurements(item),
|
|
297
|
+
}
|
|
298
|
+
return dict(chunks)
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _intrinsic_detail_entry(item) -> dict:
|
|
302
|
+
return {
|
|
303
|
+
"name": item.name,
|
|
304
|
+
"signature": item.signature,
|
|
305
|
+
"description": item.description,
|
|
306
|
+
"header": item.header,
|
|
307
|
+
"url": item.url,
|
|
308
|
+
"architecture": item.architecture,
|
|
309
|
+
"display_architecture": display_architecture(item.architecture),
|
|
310
|
+
"isa": item.isa,
|
|
311
|
+
"display_isa": display_isa(item.isa),
|
|
312
|
+
"display_isa_tokens": [display_isa([value]) for value in item.isa],
|
|
313
|
+
"instructions": item.instructions,
|
|
314
|
+
"instruction_refs": item.instruction_refs,
|
|
315
|
+
"metadata": item.metadata,
|
|
316
|
+
"doc_sections": item.doc_sections,
|
|
317
|
+
"notes": item.notes,
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
def _intrinsic_chunks(catalog: Catalog) -> dict[str, dict]:
|
|
322
|
+
"""Group full intrinsic details by bucket prefix.
|
|
323
|
+
|
|
324
|
+
Mirrors the ``detail-chunks/`` layout used for instructions so that
|
|
325
|
+
clicking an intrinsic only fetches the ~100-200 KB chunk containing
|
|
326
|
+
it rather than the ~138 MB full file.
|
|
327
|
+
"""
|
|
328
|
+
chunks: dict[str, dict] = defaultdict(dict)
|
|
329
|
+
for item in catalog.intrinsics:
|
|
330
|
+
prefix = _intrinsic_chunk_prefix(item.name)
|
|
331
|
+
chunks[prefix][item.name] = _intrinsic_detail_entry(item)
|
|
332
|
+
return dict(chunks)
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _build_stamp(catalog: Catalog) -> dict:
|
|
336
|
+
from datetime import datetime, timezone
|
|
337
|
+
import subprocess
|
|
338
|
+
git_sha = ""
|
|
339
|
+
try:
|
|
340
|
+
git_sha = subprocess.check_output(
|
|
341
|
+
["git", "rev-parse", "--short", "HEAD"],
|
|
342
|
+
cwd=Path(__file__).resolve().parent,
|
|
343
|
+
text=True,
|
|
344
|
+
stderr=subprocess.DEVNULL,
|
|
345
|
+
timeout=2,
|
|
346
|
+
).strip()
|
|
347
|
+
except (subprocess.SubprocessError, OSError):
|
|
348
|
+
git_sha = ""
|
|
349
|
+
return {
|
|
350
|
+
"version": __version__,
|
|
351
|
+
"git_sha": git_sha,
|
|
352
|
+
"catalog_generated_at": catalog.generated_at,
|
|
353
|
+
"intrinsics": len(catalog.intrinsics),
|
|
354
|
+
"instructions": len(catalog.instructions),
|
|
355
|
+
"built_at": datetime.now(timezone.utc).isoformat(),
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def export_web(catalog: Catalog, web_dir: Path) -> None:
|
|
360
|
+
"""Write the web app to *web_dir*.
|
|
361
|
+
|
|
362
|
+
Outputs:
|
|
363
|
+
* ``index.html`` -- assembled SPA
|
|
364
|
+
* ``search-index.json`` -- compact search data
|
|
365
|
+
* ``filter_spec.json`` -- shared ISA/category facets (web + CLI)
|
|
366
|
+
* ``build_stamp.json`` -- version/freshness metadata
|
|
367
|
+
* ``detail-chunks/{PREFIX}.json`` -- instruction detail chunks
|
|
368
|
+
* ``intrinsic-chunks/{PREFIX}.json`` -- intrinsic detail chunks
|
|
369
|
+
"""
|
|
370
|
+
web_dir.mkdir(parents=True, exist_ok=True)
|
|
371
|
+
|
|
372
|
+
def _write_json(path: Path, payload) -> None:
|
|
373
|
+
raw = json.dumps(payload, separators=(",", ":"), sort_keys=True).encode("utf-8")
|
|
374
|
+
path.write_bytes(raw)
|
|
375
|
+
with gzip.open(str(path) + ".gz", "wb", compresslevel=6) as fh:
|
|
376
|
+
fh.write(raw)
|
|
377
|
+
|
|
378
|
+
(web_dir / "index.html").write_text(_load_template())
|
|
379
|
+
|
|
380
|
+
_write_json(web_dir / "search-index.json", _search_payload(catalog))
|
|
381
|
+
|
|
382
|
+
filter_spec = _filter_spec_for_catalog(catalog)
|
|
383
|
+
_write_json(web_dir / "filter_spec.json", filter_spec.to_json())
|
|
384
|
+
|
|
385
|
+
_write_json(web_dir / "build_stamp.json", _build_stamp(catalog))
|
|
386
|
+
|
|
387
|
+
chunks_dir = web_dir / "detail-chunks"
|
|
388
|
+
if chunks_dir.exists():
|
|
389
|
+
shutil.rmtree(chunks_dir)
|
|
390
|
+
chunks_dir.mkdir()
|
|
391
|
+
for prefix, chunk in _detail_chunks(catalog).items():
|
|
392
|
+
_write_json(chunks_dir / f"{prefix}.json", chunk)
|
|
393
|
+
|
|
394
|
+
intrinsic_chunks_dir = web_dir / "intrinsic-chunks"
|
|
395
|
+
if intrinsic_chunks_dir.exists():
|
|
396
|
+
shutil.rmtree(intrinsic_chunks_dir)
|
|
397
|
+
intrinsic_chunks_dir.mkdir()
|
|
398
|
+
for prefix, chunk in _intrinsic_chunks(catalog).items():
|
|
399
|
+
_write_json(intrinsic_chunks_dir / f"{prefix}.json", chunk)
|
|
400
|
+
|
|
401
|
+
for stale in (web_dir / "intrinsic-details.json", web_dir / "intrinsic-details.json.gz"):
|
|
402
|
+
if stale.exists():
|
|
403
|
+
stale.unlink()
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simdref
|
|
3
|
+
Version: 0.0.0
|
|
4
|
+
Summary: Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, LSP, and static web export.
|
|
5
|
+
Author: Marco
|
|
6
|
+
License: GPL-3.0-or-later
|
|
7
|
+
Classifier: Development Status :: 3 - Alpha
|
|
8
|
+
Classifier: Environment :: Console
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Software Development :: Documentation
|
|
17
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: httpx<1,>=0.28
|
|
22
|
+
Requires-Dist: msgpack<2,>=1.0
|
|
23
|
+
Requires-Dist: pdfplumber<1,>=0.11
|
|
24
|
+
Requires-Dist: PyMuPDF<2,>=1.24
|
|
25
|
+
Requires-Dist: PyYAML<7,>=6
|
|
26
|
+
Requires-Dist: rapidfuzz<4,>=3.9
|
|
27
|
+
Requires-Dist: rich<15,>=13
|
|
28
|
+
Requires-Dist: textual<2,>=1.0
|
|
29
|
+
Requires-Dist: typer<1,>=0.19
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# simdref
|
|
33
|
+
|
|
34
|
+
[](https://github.com/DiamonDinoia/simdref/actions/workflows/ci.yml)
|
|
35
|
+
[](https://github.com/DiamonDinoia/simdref/actions/workflows/pages.yml)
|
|
36
|
+
[](https://test.pypi.org/project/simdref/)
|
|
37
|
+
[](https://test.pypi.org/project/simdref/)
|
|
38
|
+
|
|
39
|
+
A single searchable reference for SIMD intrinsics and instructions across
|
|
40
|
+
**x86 (Intel + uops.info)**, **Arm (ACLE / AARCHMRS)**, and **RISC-V
|
|
41
|
+
(RVV + unified-db)**. Runs as a CLI, a Textual TUI, an LSP server,
|
|
42
|
+
generated manpages, a static web app, and a structured JSON interface
|
|
43
|
+
for LLM skills.
|
|
44
|
+
|
|
45
|
+
[Web App](https://diamondinoia.github.io/simdref/) ·
|
|
46
|
+
[TestPyPI](https://test.pypi.org/project/simdref/) ·
|
|
47
|
+
[GitHub](https://github.com/DiamonDinoia/simdref) ·
|
|
48
|
+
[Contributing](CONTRIBUTING.md)
|
|
49
|
+
|
|
50
|
+
<!-- Screenshots are hosted on the `docs-assets` branch so the main
|
|
51
|
+
branch stays lightweight to clone. -->
|
|
52
|
+
<p align="center">
|
|
53
|
+
<img alt="simdref TUI" src="https://raw.githubusercontent.com/DiamonDinoia/simdref/docs-assets/img/tui.svg" width="720">
|
|
54
|
+
<br><em>Interactive TUI with ISA filters, ranked results, and measured/modeled performance tables.</em>
|
|
55
|
+
</p>
|
|
56
|
+
|
|
57
|
+
## Install
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
pip install simdref
|
|
61
|
+
isa update # download the pre-built catalog
|
|
62
|
+
isa doctor # confirm everything is wired up
|
|
63
|
+
isa # open the TUI
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
The package installs two equivalent executables, **`isa`** (short) and
|
|
67
|
+
**`simdref`** (explicit). The rest of this README uses `isa`.
|
|
68
|
+
|
|
69
|
+
`isa update` pulls the combined catalog (x86 measured + Arm/RISC-V
|
|
70
|
+
measured & modeled) from the latest GitHub Release — **no `llvm-mca`
|
|
71
|
+
required**. Only contributors doing a full local rebuild with
|
|
72
|
+
`isa build` need `llvm-mca` 18+ on `PATH`.
|
|
73
|
+
|
|
74
|
+
Pre-release builds live on TestPyPI:
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
pip install -i https://test.pypi.org/simple/ \
|
|
78
|
+
--extra-index-url https://pypi.org/simple/ simdref
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Quickstart
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
isa _mm_add_ps # exact intrinsic -> detailed view
|
|
85
|
+
isa VPADDD # exact instruction -> detailed view
|
|
86
|
+
isa _mm_add # fuzzy -> ranked search results
|
|
87
|
+
isa mm add # tokenized query -> intrinsic-biased search
|
|
88
|
+
isa ADD # mnemonic-like -> instruction-biased search
|
|
89
|
+
isa VADDPS 2 # pick variant #2 from the last result list
|
|
90
|
+
isa # open the interactive TUI
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Interfaces
|
|
94
|
+
|
|
95
|
+
**Web app** — a self-contained static SPA with filters and performance
|
|
96
|
+
tables, published to GitHub Pages at
|
|
97
|
+
[diamondinoia.github.io/simdref](https://diamondinoia.github.io/simdref/).
|
|
98
|
+
Export your own copy:
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
isa web --web-dir ./web
|
|
102
|
+
isa serve --web-dir ./web # gzip-aware local server
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The [live demo](https://diamondinoia.github.io/simdref/) hosts the same
|
|
106
|
+
build — search across ~122k entries with ISA filters and per-uarch perf
|
|
107
|
+
tables.
|
|
108
|
+
|
|
109
|
+
**LSP** — hover docs + completion for intrinsic names and instruction
|
|
110
|
+
mnemonics in any LSP-capable editor:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
simdref-lsp # speaks JSON-RPC over stdio
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
```lua
|
|
117
|
+
-- Neovim
|
|
118
|
+
vim.lsp.start({ name = "simdref", cmd = { ".venv/bin/simdref-lsp" } })
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
**LLM interface** — stable JSON / NDJSON for agents and editor skills,
|
|
122
|
+
with meaningful exit codes so tools can distinguish *no match* (2),
|
|
123
|
+
*ambiguous* (3), and *bad flag* (1):
|
|
124
|
+
|
|
125
|
+
```bash
|
|
126
|
+
isa llm query _mm_add_ps --source-kind measured
|
|
127
|
+
echo -e "_mm_add_ps\nVPADDD" | isa llm batch
|
|
128
|
+
isa llm list --pattern "*gather*" --isa Intel
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
See [docs/LLM.md](docs/LLM.md) for the full payload shape and a
|
|
132
|
+
Claude-skill recipe.
|
|
133
|
+
|
|
134
|
+
**Assembly annotator** — turn compiler output into a self-documented
|
|
135
|
+
`.sa` file. Given `hello_simd.s`:
|
|
136
|
+
|
|
137
|
+
```asm
|
|
138
|
+
dot8:
|
|
139
|
+
vmovups (%rdi), %ymm0
|
|
140
|
+
vmovups (%rsi), %ymm1
|
|
141
|
+
vmulps %ymm1, %ymm0, %ymm0
|
|
142
|
+
vaddps %ymm0, %ymm0, %ymm0
|
|
143
|
+
vhaddps %ymm0, %ymm0, %ymm0
|
|
144
|
+
ret
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
isa annotate hello_simd.s # writes hello_simd.sa
|
|
149
|
+
isa annotate hello_simd.s --arch skylake-x -o - # to stdout, skylake-x only
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
produces:
|
|
153
|
+
|
|
154
|
+
```asm
|
|
155
|
+
dot8:
|
|
156
|
+
vmovups (%rdi), %ymm0 # Move Unaligned Packed Single Precision FP Values. | lat=10.3c cpi=0.78 [avg of 25 archs, measured]
|
|
157
|
+
vmovups (%rsi), %ymm1 # Move Unaligned Packed Single Precision FP Values. | lat=10.3c cpi=0.78 [avg of 25 archs, measured]
|
|
158
|
+
vmulps %ymm1, %ymm0, %ymm0 # Multiply Packed Single Precision FP Values. | lat=3.8c cpi=0.54 [avg of 25 archs, measured]
|
|
159
|
+
vaddps %ymm0, %ymm0, %ymm0 # Add Packed Single Precision FP Values. | lat=3.1c cpi=0.58 [avg of 25 archs, measured]
|
|
160
|
+
vhaddps %ymm0, %ymm0, %ymm0 # Horizontal Add Packed Single Precision FP. | lat=5.6c cpi=2.22 [avg of 25 archs, measured]
|
|
161
|
+
ret
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The output is still valid assembly — comments start with `#`, so `as`
|
|
165
|
+
and `ld` still consume it.
|
|
166
|
+
|
|
167
|
+
## Commands
|
|
168
|
+
|
|
169
|
+
`isa --help` groups commands into **Commands** (day-to-day) and
|
|
170
|
+
**Dev commands** (rebuild / export / completion).
|
|
171
|
+
|
|
172
|
+
**Commands**
|
|
173
|
+
|
|
174
|
+
| Command | Description |
|
|
175
|
+
|---------|-------------|
|
|
176
|
+
| `isa` / `isa <query>` | Open the TUI, pre-filling the query when one is given |
|
|
177
|
+
| `isa doctor` | Check the installation — pass/fail per component, non-zero exit on failure |
|
|
178
|
+
| `isa update` | Download the pre-built release catalog (no `llvm-mca` required) |
|
|
179
|
+
| `isa llm query <q>` | Strict lookup → JSON/NDJSON/Markdown (see [docs/LLM.md](docs/LLM.md)) |
|
|
180
|
+
| `isa llm batch` | Resolve many queries from stdin in one invocation (NDJSON out) |
|
|
181
|
+
| `isa llm list` | Dump the `FilterSpec` or stream matching catalog entries |
|
|
182
|
+
| `isa llm schema` | Print the JSON schema for `llm` payloads |
|
|
183
|
+
| `isa annotate <file.s>` | Annotate a `.s` assembly file with per-instruction summaries and latency/CPI — writes `<file>.sa` |
|
|
184
|
+
|
|
185
|
+
**Dev commands**
|
|
186
|
+
|
|
187
|
+
| Command | Description |
|
|
188
|
+
|---------|-------------|
|
|
189
|
+
| `isa build` | Full local rebuild from upstream sources, including Intel SDM parsing (`llvm-mca` 18+ required) |
|
|
190
|
+
| `isa web` | Export the static web app under `web/` |
|
|
191
|
+
| `isa serve` | Serve the exported web app locally (gzip-aware) |
|
|
192
|
+
| `isa completion install [SHELL]` | Install shell completion into the user's profile |
|
|
193
|
+
| `isa completion show [SHELL]` | Print the completion script for a shell |
|
|
194
|
+
|
|
195
|
+
## Data sources
|
|
196
|
+
|
|
197
|
+
| Source | What | Entries¹ |
|
|
198
|
+
|--------|------|----------|
|
|
199
|
+
| Intel Intrinsics Guide | Signatures, descriptions, ISA, categories | 7,146 intrinsics |
|
|
200
|
+
| uops.info | Instructions, operands, latency, throughput, ports | 22,276 instructions |
|
|
201
|
+
| Arm ACLE (NEON/SVE) | Intrinsic signatures and descriptions | 10,791 intrinsics |
|
|
202
|
+
| Arm AARCHMRS (A64) | Base instruction forms and operand tables | live-only² |
|
|
203
|
+
| riscv-rvv-intrinsic-doc | RVV intrinsics with deterministic instruction refs | 74,319 intrinsics |
|
|
204
|
+
| RISC-V unified-db | RVV instruction forms, ISA tags, Description/Operation | 2,868 instructions |
|
|
205
|
+
|
|
206
|
+
¹ Counts from the current vendored snapshot. See
|
|
207
|
+
[`docs/coverage/summary.json`](docs/coverage/summary.json) for live
|
|
208
|
+
parity against upstream and [`docs/SOURCES.md`](docs/SOURCES.md) for
|
|
209
|
+
licenses and refresh cadence.
|
|
210
|
+
|
|
211
|
+
² The full AARCHMRS A64 spec is only available via live fetch or by
|
|
212
|
+
dropping the tarball under `vendor/arm/`.
|
|
213
|
+
|
|
214
|
+
Every rendered latency / CPI is tagged `(measured, <core>)` or
|
|
215
|
+
`(modeled, <core>)` so measured and modeled numbers never get silently
|
|
216
|
+
mixed.
|
|
217
|
+
|
|
218
|
+
### Scope caveats
|
|
219
|
+
|
|
220
|
+
- Performance data is x86-only in v1.
|
|
221
|
+
- RISC-V coverage is RVV-focused — not full scalar or privileged ISA.
|
|
222
|
+
|
|
223
|
+
## Development
|
|
224
|
+
|
|
225
|
+
```bash
|
|
226
|
+
git clone https://github.com/DiamonDinoia/simdref.git
|
|
227
|
+
cd simdref
|
|
228
|
+
python3 -m venv .venv
|
|
229
|
+
.venv/bin/pip install -e .
|
|
230
|
+
.venv/bin/isa build # requires llvm-mca 18+
|
|
231
|
+
.venv/bin/python -m pytest tests/ -v
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md) for the full dev flow
|
|
235
|
+
(tests, adding a new source, build stages) and
|
|
236
|
+
[ARCHITECTURE.md](ARCHITECTURE.md) for module layout.
|
|
237
|
+
|
|
238
|
+
## License
|
|
239
|
+
|
|
240
|
+
[GNU General Public License v3.0](LICENSE).
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
simdref/__init__.py,sha256=AKGa4KA4aQgfyQ1tmwAZ18REXz74KTFq038f9Zhurd8,74
|
|
2
|
+
simdref/__main__.py,sha256=DH7e8WtEXN2HJM9BaZRLcdsaWXiU01AtnPMxaj4McXQ,88
|
|
3
|
+
simdref/annotate.py,sha256=fD1XleB1dwXb72ZmK_MeBOQy1q_b2jWOP1BAeQ4LAOQ,13857
|
|
4
|
+
simdref/arm_instructions.py,sha256=5pd6KyICSJ6dfUgDAdBg2WxgTLB5loZsWE9N79wZRhQ,15488
|
|
5
|
+
simdref/cli.py,sha256=g06ST1nporfpIECIZOt65WdsnSN5GmF1IB3haIdman4,63266
|
|
6
|
+
simdref/display.py,sha256=oxKkZxHEaYqHCG5wsh-fluGV3no52S88agtrEwllbto,35563
|
|
7
|
+
simdref/filters.py,sha256=nN3wwrneQUOnuSiWGNoe-8g7WFfhWV5piDhql2sGIHo,12189
|
|
8
|
+
simdref/ingest.py,sha256=_ClfCBEqRDkWBe1WQpTbzIFpsDduLhzA6GXbhW9CFwc,3532
|
|
9
|
+
simdref/ingest_catalog.py,sha256=pv7P4sXg4wsiKkvtERz-Zi9PsQECKA0WLFn9G1TBBE8,52310
|
|
10
|
+
simdref/ingest_pdf.py,sha256=rMqpWdeU6BK7Nqtj_t4dEAC1G56-4-j9_tR-twYyVG8,6696
|
|
11
|
+
simdref/ingest_sources.py,sha256=7lrEIt-xEUWYNt0_B_QkaiN6XVLLWsx26-9Q2GBuDxE,22144
|
|
12
|
+
simdref/lsp.py,sha256=ytC4IY2nvyLzMBO-JsV3w4Dllu3-mmI4jOnXAn40e5U,8005
|
|
13
|
+
simdref/manpages.py,sha256=blBKC5f01FwG_Y9_cEA16h8uOK_MeoxnK5qCPfQoUjU,6659
|
|
14
|
+
simdref/models.py,sha256=rDDjOVHwy4PiyjxyK8V1Hq3xqemS3AlQKHo1_-E9jh8,7951
|
|
15
|
+
simdref/pdfrefs.py,sha256=I6p7XGIXyp1QLA5gwJ3-NKzAejgUhMX7TnKbtKJfKjA,3208
|
|
16
|
+
simdref/perf.py,sha256=MB5r2mQIEGYvkladNsEz0dYO8AEAOBz8Lehx0kLXR9w,8084
|
|
17
|
+
simdref/queries.py,sha256=54s-r6uWpMVttDpjlGqX4QT4OGIzclU16hALbJ5jgGA,7144
|
|
18
|
+
simdref/riscv.py,sha256=i62HZbQKc89SuCLjlj9EY0_uxOZM28ml-lHwzddt82g,17080
|
|
19
|
+
simdref/search.py,sha256=JF_hyA65YDHNYQ7rAEQ7ufZNcbLxWh25352nQZ7l-gM,10546
|
|
20
|
+
simdref/storage.py,sha256=1twUElPj2sHYHqUyDRmkPQBsCBlCgF4qQ4y2fpHFnmE,18501
|
|
21
|
+
simdref/tui.py,sha256=Tew6-RZ58-2DgR7QoZdXemCPXaE_Dp-Nbu70QzTCe_Y,92361
|
|
22
|
+
simdref/web.py,sha256=ztnI6nImJSqcN3p7VPEQpnudcjCizoz4glPyb9U19_0,14851
|
|
23
|
+
simdref/pdfparse/__init__.py,sha256=P_25a2FQkjTB1xAjMCbfEyIq34qN4P8qOMe-cs5GA7w,419
|
|
24
|
+
simdref/pdfparse/base.py,sha256=ODgZjrrDUyFhh_4LH6IQMLO5FarGovK38don1VB2Aog,4357
|
|
25
|
+
simdref/pdfparse/intel.py,sha256=3-1vKISBs0VQvfXRIBfufd5BntVFTHt-jc4DsLQtRJU,23270
|
|
26
|
+
simdref/pdfparse/registry.py,sha256=xbotrCuwN2in6W1xalJXPETmJ2TzatTIr7uBkZcO_lw,439
|
|
27
|
+
simdref/pdfparse/types.py,sha256=S7U8uyRKKqHgO0x3mpi4hmrBKOIc-zIxC9vwOIc7CwM,2727
|
|
28
|
+
simdref/perf_sources/__init__.py,sha256=kDYVXv_uNiZ9P2srZrp4cE8XgqeXWns5Di8Ph52D-dY,1555
|
|
29
|
+
simdref/perf_sources/cores.py,sha256=XfKdxPvuM-M4C35YLILuFEo6aBeFK8bPzn9swMVLNMY,4804
|
|
30
|
+
simdref/perf_sources/llvm_mca.py,sha256=YYhIWPEMMnOSc0lIJtRZdgn8rpSNoVDjOofrV494iPM,6577
|
|
31
|
+
simdref/perf_sources/llvm_scheduling.py,sha256=cAS79SqFO9E_54osdt4p1U_7NpHfEzhvFza0JJMoKg8,23182
|
|
32
|
+
simdref/perf_sources/merge.py,sha256=2rJf8PeAwFVzyRcEkkZXllKdQ0vgQ-Ww3U9-sEsAqYw,4183
|
|
33
|
+
simdref/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
34
|
+
simdref/templates/app.js,sha256=faU1joYcu23-sAzujeEwesyzrxqTrrmW3nqs7fLWUCs,66448
|
|
35
|
+
simdref/templates/favicon.svg,sha256=3G9onI-56E0vT0iGGEMww0k7JNJsF_aKekMFhPT9DcA,319
|
|
36
|
+
simdref/templates/index.html,sha256=WcCbsnP49uVZYd5xboSJGMYEId9K63I1b-Rdz0xNASE,5939
|
|
37
|
+
simdref/templates/logo.svg,sha256=Q4t4v_F2DRgCHEcOsSGWz8VO8_5dCieLrwB1mDnb-8Q,1111
|
|
38
|
+
simdref/templates/style.css,sha256=14gP2IbCRQ3Z2621Gv1y0sX3Ci1csfb0I5aE9rWxC9I,15952
|
|
39
|
+
simdref-0.0.0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
40
|
+
simdref-0.0.0.dist-info/METADATA,sha256=a6QoPzp78Z9VTY1Q1bsjs0DzpLRW65W97IEPS0NXKyQ,9234
|
|
41
|
+
simdref-0.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
42
|
+
simdref-0.0.0.dist-info/entry_points.txt,sha256=CS3tB1z18jjiCWTh-F73pN2lKUCmLOCMWzbaiADP6og,99
|
|
43
|
+
simdref-0.0.0.dist-info/top_level.txt,sha256=eU7qjsZlLukdRacjuNm6ahYiuMKjE4G_LoWMqsJZmqU,8
|
|
44
|
+
simdref-0.0.0.dist-info/RECORD,,
|