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/filters.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""Shared filter specification for intrinsic/instruction lookups.
|
|
2
|
+
|
|
3
|
+
Single source of truth for the ISA-family / sub-ISA / category facets used by
|
|
4
|
+
the TUI, web SPA, CLI, and LLM subcommands. The Python constants here are
|
|
5
|
+
re-exported through :mod:`simdref.display` for back-compat and serialised to
|
|
6
|
+
``filter_spec.json`` for the static web bundle.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from dataclasses import dataclass, field
|
|
12
|
+
import sqlite3
|
|
13
|
+
from typing import Any, Iterable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
ISA_FAMILY_ORDER: dict[str, int] = {
|
|
17
|
+
"x86": 0, "MMX": 1, "SSE": 2, "AVX": 3,
|
|
18
|
+
"AVX-512": 4, "AVX10": 5, "AMX": 6, "APX": 7,
|
|
19
|
+
"Arm": 8, "RISC-V": 9, "SVML": 10, "Other": 11,
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
DEFAULT_ENABLED_ISAS: tuple[str, ...] = ("SSE", "AVX", "AVX-512", "Arm", "RISC-V")
|
|
23
|
+
|
|
24
|
+
FAMILY_SUB_ORDER: dict[str, list[str]] = {
|
|
25
|
+
"SSE": ["SSE", "SSE2", "SSE3", "SSSE3", "SSE4.1", "SSE4.2"],
|
|
26
|
+
"AVX": ["AVX", "AVX2", "FMA", "F16C", "AVX_VNNI", "AVX_VNNI_INT8",
|
|
27
|
+
"AVX_VNNI_INT16", "AVX_IFMA", "AVX_NE_CONVERT"],
|
|
28
|
+
"AVX-512": [
|
|
29
|
+
"AVX512F", "AVX512VL", "AVX512BW", "AVX512DQ", "AVX512CD",
|
|
30
|
+
"AVX512_VNNI", "AVX512_FP16", "AVX512_BF16",
|
|
31
|
+
"AVX512_VBMI", "AVX512_VBMI2", "AVX512_BITALG",
|
|
32
|
+
"AVX512IFMA52", "AVX512VPOPCNTDQ", "AVX512_VP2INTERSECT",
|
|
33
|
+
"VAES", "VPCLMULQDQ", "GFNI",
|
|
34
|
+
],
|
|
35
|
+
"AMX": ["AMX-TILE", "AMX-INT8", "AMX-BF16", "AMX-FP16", "AMX-COMPLEX"],
|
|
36
|
+
"Arm": ["NEON", "SVE", "SVE2"],
|
|
37
|
+
"RISC-V": ["V", "ZVE", "ZV"],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
DEFAULT_SUBS: dict[str, set[str]] = {
|
|
41
|
+
"SSE": {"SSE", "SSE2", "SSE3", "SSSE3", "SSE4.1", "SSE4.2"},
|
|
42
|
+
"AVX": {"AVX", "AVX2", "FMA", "F16C"},
|
|
43
|
+
"AVX-512": {"AVX512F", "AVX512VL", "AVX512BW", "AVX512DQ", "AVX512CD"},
|
|
44
|
+
"AMX": {"AMX-TILE", "AMX-INT8", "AMX-BF16"},
|
|
45
|
+
"Arm": {"NEON", "SVE", "SVE2"},
|
|
46
|
+
"RISC-V": {"V", "ZVE", "ZV"},
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# Strict x86-64-v4 sub-ISAs — what ships on every psABI-v4 target.
|
|
51
|
+
# Explicitly excludes: AVX-512 _VNNI/_FP16/_BF16/_VBMI*/_VPOPCNTDQ/_BITALG/
|
|
52
|
+
# _IFMA/_VP2INTERSECT, VAES, VPCLMULQDQ, GFNI, AMX, APX, AVX10, SVML.
|
|
53
|
+
X86_64_V4_SUBS: set[str] = {
|
|
54
|
+
# psABI x86-64-v2
|
|
55
|
+
"SSE", "SSE2", "SSE3", "SSSE3", "SSE4.1", "SSE4.2", "POPCNT",
|
|
56
|
+
# psABI x86-64-v3
|
|
57
|
+
"AVX", "AVX2", "F16C", "FMA", "BMI1", "BMI2", "LZCNT", "MOVBE",
|
|
58
|
+
# psABI x86-64-v4
|
|
59
|
+
"AVX512F", "AVX512VL", "AVX512BW", "AVX512DQ", "AVX512CD",
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
ARM_ALL_SUBS: set[str] = {"NEON", "SVE", "SVE2", "SME", "MVE"}
|
|
63
|
+
ARM32_SUBS: set[str] = {"NEON", "MVE"}
|
|
64
|
+
ARM64_SUBS: set[str] = {"NEON", "SVE", "SVE2", "SME"}
|
|
65
|
+
RVV_SUBS: set[str] = {"V", "ZVE", "ZV"}
|
|
66
|
+
|
|
67
|
+
# Default preset mirrors DEFAULT_ENABLED_ISAS but uses strict-v4 on x86.
|
|
68
|
+
DEFAULT_PRESET_SUBS: set[str] = X86_64_V4_SUBS | ARM_ALL_SUBS | RVV_SUBS
|
|
69
|
+
|
|
70
|
+
# arm_arch facet values (NULL excluded; filter logic handles missing values).
|
|
71
|
+
ARM_ARCH_VALUES: tuple[str, ...] = ("A32", "A64", "BOTH")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
@dataclass(frozen=True)
|
|
75
|
+
class PresetSpec:
|
|
76
|
+
"""Declarative preset: families + subs + arm_arch + kind applied atomically."""
|
|
77
|
+
|
|
78
|
+
families: frozenset[str]
|
|
79
|
+
subs: frozenset[str]
|
|
80
|
+
arm_arch: frozenset[str] | None # None = no arm_arch filter
|
|
81
|
+
kind: frozenset[str]
|
|
82
|
+
|
|
83
|
+
def to_dict(self) -> dict[str, Any]:
|
|
84
|
+
return {
|
|
85
|
+
"families": sorted(self.families),
|
|
86
|
+
"subs": sorted(self.subs),
|
|
87
|
+
"arm_arch": sorted(self.arm_arch) if self.arm_arch is not None else None,
|
|
88
|
+
"kind": sorted(self.kind),
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
@classmethod
|
|
92
|
+
def empty(cls) -> "PresetSpec":
|
|
93
|
+
return cls(frozenset(), frozenset(), None, frozenset())
|
|
94
|
+
|
|
95
|
+
@classmethod
|
|
96
|
+
def all(cls) -> "PresetSpec":
|
|
97
|
+
return cls(
|
|
98
|
+
families=frozenset(ISA_FAMILY_ORDER.keys()),
|
|
99
|
+
subs=frozenset(sub for subs in FAMILY_SUB_ORDER.values() for sub in subs)
|
|
100
|
+
| ARM_ALL_SUBS,
|
|
101
|
+
arm_arch=None,
|
|
102
|
+
kind=frozenset({"intrinsic", "instruction"}),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
ARCH_PRESETS: dict[str, PresetSpec] = {
|
|
107
|
+
"default": PresetSpec(
|
|
108
|
+
families=frozenset({"SSE", "AVX", "AVX-512", "Arm", "RISC-V"}),
|
|
109
|
+
subs=frozenset(DEFAULT_PRESET_SUBS),
|
|
110
|
+
arm_arch=None,
|
|
111
|
+
kind=frozenset({"intrinsic"}),
|
|
112
|
+
),
|
|
113
|
+
"intel": PresetSpec(
|
|
114
|
+
# Include "x86" so BMI/POPCNT/LZCNT intrinsics (family=x86) are reachable.
|
|
115
|
+
families=frozenset({"x86", "SSE", "AVX", "AVX-512"}),
|
|
116
|
+
subs=frozenset(X86_64_V4_SUBS),
|
|
117
|
+
arm_arch=None,
|
|
118
|
+
kind=frozenset({"intrinsic"}),
|
|
119
|
+
),
|
|
120
|
+
"arm32": PresetSpec(
|
|
121
|
+
families=frozenset({"Arm"}),
|
|
122
|
+
subs=frozenset(ARM32_SUBS),
|
|
123
|
+
arm_arch=frozenset({"A32", "BOTH"}),
|
|
124
|
+
kind=frozenset({"intrinsic"}),
|
|
125
|
+
),
|
|
126
|
+
"arm64": PresetSpec(
|
|
127
|
+
families=frozenset({"Arm"}),
|
|
128
|
+
subs=frozenset(ARM64_SUBS),
|
|
129
|
+
arm_arch=frozenset({"A64", "BOTH"}),
|
|
130
|
+
kind=frozenset({"intrinsic"}),
|
|
131
|
+
),
|
|
132
|
+
"riscv": PresetSpec(
|
|
133
|
+
families=frozenset({"RISC-V"}),
|
|
134
|
+
subs=frozenset(RVV_SUBS),
|
|
135
|
+
arm_arch=None,
|
|
136
|
+
kind=frozenset({"intrinsic"}),
|
|
137
|
+
),
|
|
138
|
+
"none": PresetSpec.empty(),
|
|
139
|
+
"all": PresetSpec.all(),
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@dataclass(frozen=True)
|
|
144
|
+
class CategorySpec:
|
|
145
|
+
"""A category facet derived from the catalog database."""
|
|
146
|
+
|
|
147
|
+
family: str
|
|
148
|
+
category: str
|
|
149
|
+
subcategory: str = ""
|
|
150
|
+
count: int = 0
|
|
151
|
+
|
|
152
|
+
def to_dict(self) -> dict[str, Any]:
|
|
153
|
+
return {
|
|
154
|
+
"family": self.family,
|
|
155
|
+
"category": self.category,
|
|
156
|
+
"subcategory": self.subcategory,
|
|
157
|
+
"count": self.count,
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
@dataclass
|
|
162
|
+
class FilterSpec:
|
|
163
|
+
"""Filterable facets usable by TUI, web, CLI, and LLM consumers."""
|
|
164
|
+
|
|
165
|
+
family_order: dict[str, int] = field(default_factory=lambda: dict(ISA_FAMILY_ORDER))
|
|
166
|
+
family_sub_order: dict[str, list[str]] = field(
|
|
167
|
+
default_factory=lambda: {k: list(v) for k, v in FAMILY_SUB_ORDER.items()}
|
|
168
|
+
)
|
|
169
|
+
default_enabled: tuple[str, ...] = DEFAULT_ENABLED_ISAS
|
|
170
|
+
default_subs: dict[str, set[str]] = field(
|
|
171
|
+
default_factory=lambda: {k: set(v) for k, v in DEFAULT_SUBS.items()}
|
|
172
|
+
)
|
|
173
|
+
categories: list[CategorySpec] = field(default_factory=list)
|
|
174
|
+
presets: dict[str, PresetSpec] = field(
|
|
175
|
+
default_factory=lambda: dict(ARCH_PRESETS)
|
|
176
|
+
)
|
|
177
|
+
arm_arch_values: tuple[str, ...] = ARM_ARCH_VALUES
|
|
178
|
+
|
|
179
|
+
def to_json(self) -> dict[str, Any]:
|
|
180
|
+
"""JSON-friendly projection used by the web SPA."""
|
|
181
|
+
return {
|
|
182
|
+
"family_order": dict(self.family_order),
|
|
183
|
+
"family_sub_order": {k: list(v) for k, v in self.family_sub_order.items()},
|
|
184
|
+
"default_enabled": list(self.default_enabled),
|
|
185
|
+
"default_subs": {k: sorted(v) for k, v in self.default_subs.items()},
|
|
186
|
+
"categories": [c.to_dict() for c in self.categories],
|
|
187
|
+
"presets": {name: spec.to_dict() for name, spec in self.presets.items()},
|
|
188
|
+
"arm_arch_values": list(self.arm_arch_values),
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
# --- in-memory predicate --------------------------------------------------
|
|
192
|
+
|
|
193
|
+
def matches(
|
|
194
|
+
self,
|
|
195
|
+
record: Any,
|
|
196
|
+
enabled_families: Iterable[str] | None = None,
|
|
197
|
+
enabled_categories: Iterable[str] | None = None,
|
|
198
|
+
) -> bool:
|
|
199
|
+
"""Return whether *record* passes the given family/category filter.
|
|
200
|
+
|
|
201
|
+
Accepts anything with an ``isa`` iterable and optional ``category``.
|
|
202
|
+
"""
|
|
203
|
+
families = set(enabled_families) if enabled_families is not None else None
|
|
204
|
+
categories = set(enabled_categories) if enabled_categories is not None else None
|
|
205
|
+
if families is not None:
|
|
206
|
+
from simdref.display import isa_family # local import avoids cycle
|
|
207
|
+
record_families = {isa_family(v) for v in (getattr(record, "isa", None) or [])}
|
|
208
|
+
if not record_families & families:
|
|
209
|
+
return False
|
|
210
|
+
if categories is not None:
|
|
211
|
+
record_category = getattr(record, "category", None)
|
|
212
|
+
if not record_category:
|
|
213
|
+
record_category = (getattr(record, "metadata", {}) or {}).get("category", "")
|
|
214
|
+
if record_category not in categories:
|
|
215
|
+
return False
|
|
216
|
+
return True
|
|
217
|
+
|
|
218
|
+
# --- SQL pushdown helper --------------------------------------------------
|
|
219
|
+
|
|
220
|
+
def sql_predicate(
|
|
221
|
+
self,
|
|
222
|
+
table: str,
|
|
223
|
+
enabled_families: Iterable[str] | None = None,
|
|
224
|
+
enabled_categories: Iterable[str] | None = None,
|
|
225
|
+
enabled_arm_arch: Iterable[str] | None = None,
|
|
226
|
+
) -> tuple[str, list[Any]]:
|
|
227
|
+
"""Return a SQL ``WHERE`` fragment and bind values for *table*.
|
|
228
|
+
|
|
229
|
+
Both ``intrinsics_data`` and ``instructions_data`` carry an indexed
|
|
230
|
+
``category`` column (schema v10+). ISA family filtering relies on
|
|
231
|
+
substring match against the space-joined ``isa`` column.
|
|
232
|
+
"""
|
|
233
|
+
clauses: list[str] = []
|
|
234
|
+
binds: list[Any] = []
|
|
235
|
+
if enabled_families:
|
|
236
|
+
family_tokens: set[str] = set()
|
|
237
|
+
for fam in enabled_families:
|
|
238
|
+
family_tokens.update(self.family_sub_order.get(fam, []))
|
|
239
|
+
family_tokens.add(fam)
|
|
240
|
+
if family_tokens:
|
|
241
|
+
clauses.append(
|
|
242
|
+
"(" + " OR ".join(f"{table}.isa LIKE ?" for _ in family_tokens) + ")"
|
|
243
|
+
)
|
|
244
|
+
binds.extend(f"%{tok}%" for tok in family_tokens)
|
|
245
|
+
if enabled_categories and table in ("intrinsics_data", "instructions_data"):
|
|
246
|
+
categories_list = list(enabled_categories)
|
|
247
|
+
if categories_list:
|
|
248
|
+
placeholders = ",".join("?" for _ in categories_list)
|
|
249
|
+
clauses.append(f"{table}.category IN ({placeholders})")
|
|
250
|
+
binds.extend(categories_list)
|
|
251
|
+
if enabled_arm_arch and table == "intrinsics_data":
|
|
252
|
+
arch_list = list(enabled_arm_arch)
|
|
253
|
+
if arch_list:
|
|
254
|
+
placeholders = ",".join("?" for _ in arch_list)
|
|
255
|
+
clauses.append(f"{table}.arm_arch IN ({placeholders})")
|
|
256
|
+
binds.extend(arch_list)
|
|
257
|
+
return (" AND ".join(clauses), binds)
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
# ---------------------------------------------------------------------------
|
|
261
|
+
# DB-backed category aggregation
|
|
262
|
+
# ---------------------------------------------------------------------------
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def load_categories_from_db(conn: sqlite3.Connection) -> list[CategorySpec]:
|
|
266
|
+
"""Derive the category facet list from ``intrinsics_data``.
|
|
267
|
+
|
|
268
|
+
Runs at build time so categories are data-driven rather than hardcoded.
|
|
269
|
+
Instruction-side categories are reached through ``metadata->>'category'``
|
|
270
|
+
on the payload and are skipped here (schema lacks an indexed column).
|
|
271
|
+
"""
|
|
272
|
+
from simdref.display import isa_family # local import avoids cycle
|
|
273
|
+
queries = [
|
|
274
|
+
"""
|
|
275
|
+
SELECT category, subcategory, isa, COUNT(*) AS n
|
|
276
|
+
FROM intrinsics_data
|
|
277
|
+
WHERE category != ''
|
|
278
|
+
GROUP BY category, subcategory, isa
|
|
279
|
+
""",
|
|
280
|
+
"""
|
|
281
|
+
SELECT category, '' AS subcategory, isa, COUNT(*) AS n
|
|
282
|
+
FROM instructions_data
|
|
283
|
+
WHERE category != ''
|
|
284
|
+
GROUP BY category, isa
|
|
285
|
+
""",
|
|
286
|
+
]
|
|
287
|
+
aggregate: dict[tuple[str, str, str], int] = {}
|
|
288
|
+
for sql in queries:
|
|
289
|
+
try:
|
|
290
|
+
rows = conn.execute(sql).fetchall()
|
|
291
|
+
except sqlite3.Error:
|
|
292
|
+
continue
|
|
293
|
+
for row in rows:
|
|
294
|
+
isa_str = row["isa"] if hasattr(row, "keys") else row[2]
|
|
295
|
+
category = row["category"] if hasattr(row, "keys") else row[0]
|
|
296
|
+
subcategory = row["subcategory"] if hasattr(row, "keys") else row[1]
|
|
297
|
+
count = row["n"] if hasattr(row, "keys") else row[3]
|
|
298
|
+
tokens = [t for t in str(isa_str or "").split() if t]
|
|
299
|
+
families = {isa_family(t) for t in tokens} or {"Other"}
|
|
300
|
+
for family in families:
|
|
301
|
+
key = (family, category, subcategory)
|
|
302
|
+
aggregate[key] = aggregate.get(key, 0) + int(count)
|
|
303
|
+
specs = [
|
|
304
|
+
CategorySpec(family=fam, category=cat, subcategory=sub, count=n)
|
|
305
|
+
for (fam, cat, sub), n in sorted(aggregate.items(), key=lambda kv: (kv[0][0], kv[0][1], kv[0][2]))
|
|
306
|
+
]
|
|
307
|
+
return specs
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
def build_filter_spec(conn: sqlite3.Connection | None = None) -> FilterSpec:
|
|
311
|
+
"""Build a :class:`FilterSpec`, deriving categories from *conn* if given."""
|
|
312
|
+
spec = FilterSpec()
|
|
313
|
+
if conn is not None:
|
|
314
|
+
try:
|
|
315
|
+
spec.categories = load_categories_from_db(conn)
|
|
316
|
+
except sqlite3.Error:
|
|
317
|
+
spec.categories = []
|
|
318
|
+
return spec
|
simdref/ingest.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""Public ingest entrypoints and compatibility wrappers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Callable
|
|
8
|
+
|
|
9
|
+
import msgpack
|
|
10
|
+
|
|
11
|
+
from simdref.ingest_catalog import (
|
|
12
|
+
_instruction_summary,
|
|
13
|
+
_normalize_operand_xtype,
|
|
14
|
+
build_catalog,
|
|
15
|
+
link_records,
|
|
16
|
+
parse_intel_payload,
|
|
17
|
+
parse_riscv_instruction_payload,
|
|
18
|
+
parse_riscv_intrinsics_payload,
|
|
19
|
+
parse_uops_xml,
|
|
20
|
+
)
|
|
21
|
+
from simdref.ingest_pdf import merge_pdf_enrichment
|
|
22
|
+
from simdref.ingest_sources import (
|
|
23
|
+
fetch_intel_data,
|
|
24
|
+
fetch_riscv_rvv_intrinsics_data,
|
|
25
|
+
fetch_riscv_unified_db_data,
|
|
26
|
+
fetch_uops_xml,
|
|
27
|
+
now_iso,
|
|
28
|
+
)
|
|
29
|
+
from simdref.pdfparse.intel import INTEL_SDM_CACHE_PATH, INTEL_SDM_URL, parse_intel_sdm
|
|
30
|
+
from simdref.storage import ensure_dir
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _sha256_file(path: Path) -> str:
|
|
34
|
+
hasher = hashlib.sha256()
|
|
35
|
+
with path.open("rb") as fh:
|
|
36
|
+
for chunk in iter(lambda: fh.read(1024 * 1024), b""):
|
|
37
|
+
hasher.update(chunk)
|
|
38
|
+
return hasher.hexdigest()
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_or_parse_intel_sdm(
|
|
42
|
+
pdf_path: Path,
|
|
43
|
+
*,
|
|
44
|
+
status: Callable[[str], None] | None = None,
|
|
45
|
+
cache_path: Path = INTEL_SDM_CACHE_PATH,
|
|
46
|
+
) -> dict[str, dict[str, object]]:
|
|
47
|
+
if cache_path.exists():
|
|
48
|
+
try:
|
|
49
|
+
payload = msgpack.unpackb(cache_path.read_bytes(), raw=False)
|
|
50
|
+
if payload.get("pdf_url") == INTEL_SDM_URL and payload.get("pdf_sha256") == _sha256_file(pdf_path):
|
|
51
|
+
descriptions = payload.get("descriptions")
|
|
52
|
+
if isinstance(descriptions, dict):
|
|
53
|
+
return descriptions
|
|
54
|
+
except Exception:
|
|
55
|
+
pass
|
|
56
|
+
descriptions = parse_intel_sdm(pdf_path, status=status)
|
|
57
|
+
if hasattr(descriptions, "descriptions"):
|
|
58
|
+
descriptions = {
|
|
59
|
+
key: {
|
|
60
|
+
"sections": value.sections,
|
|
61
|
+
"source_url": value.source_url,
|
|
62
|
+
"page_start": value.page_start,
|
|
63
|
+
"page_end": value.page_end,
|
|
64
|
+
}
|
|
65
|
+
for key, value in descriptions.descriptions.items()
|
|
66
|
+
}
|
|
67
|
+
ensure_dir(cache_path.parent)
|
|
68
|
+
payload = {
|
|
69
|
+
"pdf_url": INTEL_SDM_URL,
|
|
70
|
+
"pdf_sha256": _sha256_file(pdf_path),
|
|
71
|
+
"descriptions": descriptions,
|
|
72
|
+
}
|
|
73
|
+
cache_path.write_bytes(msgpack.packb(payload, use_bin_type=True))
|
|
74
|
+
return descriptions
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _merge_descriptions(
|
|
78
|
+
instructions,
|
|
79
|
+
descriptions: dict[str, dict[str, object]],
|
|
80
|
+
) -> None:
|
|
81
|
+
from simdref.pdfparse.types import PdfDescriptionPayload, PdfEnrichmentResult
|
|
82
|
+
|
|
83
|
+
result = PdfEnrichmentResult(
|
|
84
|
+
descriptions={
|
|
85
|
+
key: PdfDescriptionPayload(
|
|
86
|
+
sections=dict(value.get("sections") or {}),
|
|
87
|
+
source_url=str(value.get("source_url") or "https://cdrdv2.intel.com/v1/dl/getContent/671200"),
|
|
88
|
+
page_start=value.get("page_start") if isinstance(value.get("page_start"), int) else None,
|
|
89
|
+
page_end=value.get("page_end") if isinstance(value.get("page_end"), int) else None,
|
|
90
|
+
)
|
|
91
|
+
for key, value in descriptions.items()
|
|
92
|
+
}
|
|
93
|
+
)
|
|
94
|
+
merge_pdf_enrichment(instructions, "intel-sdm", result)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
__all__ = [
|
|
98
|
+
"build_catalog",
|
|
99
|
+
"fetch_intel_data",
|
|
100
|
+
"fetch_riscv_rvv_intrinsics_data",
|
|
101
|
+
"fetch_riscv_unified_db_data",
|
|
102
|
+
"fetch_uops_xml",
|
|
103
|
+
"link_records",
|
|
104
|
+
"load_or_parse_intel_sdm",
|
|
105
|
+
"_instruction_summary",
|
|
106
|
+
"_normalize_operand_xtype",
|
|
107
|
+
"now_iso",
|
|
108
|
+
"parse_intel_sdm",
|
|
109
|
+
"parse_intel_payload",
|
|
110
|
+
"parse_riscv_instruction_payload",
|
|
111
|
+
"parse_riscv_intrinsics_payload",
|
|
112
|
+
"parse_uops_xml",
|
|
113
|
+
]
|