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/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
+ [![CI](https://github.com/DiamonDinoia/simdref/actions/workflows/ci.yml/badge.svg)](https://github.com/DiamonDinoia/simdref/actions/workflows/ci.yml)
35
+ [![Pages](https://github.com/DiamonDinoia/simdref/actions/workflows/pages.yml/badge.svg)](https://github.com/DiamonDinoia/simdref/actions/workflows/pages.yml)
36
+ [![TestPyPI](https://img.shields.io/pypi/v/simdref?pypiBaseUrl=https%3A%2F%2Ftest.pypi.org&label=TestPyPI)](https://test.pypi.org/project/simdref/)
37
+ [![Python](https://img.shields.io/pypi/pyversions/simdref?pypiBaseUrl=https%3A%2F%2Ftest.pypi.org)](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,,