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/storage.py
ADDED
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
"""Msgpack and SQLite persistence for the simdref catalog.
|
|
2
|
+
|
|
3
|
+
Stores a compact msgpack snapshot for reuse and a SQLite database with
|
|
4
|
+
FTS5 virtual tables for fast full-text search with BM25 ranking.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import sqlite3
|
|
12
|
+
import sys
|
|
13
|
+
from itertools import islice
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
import msgpack
|
|
17
|
+
|
|
18
|
+
from simdref.models import Catalog, InstructionRecord, IntrinsicRecord, SourceVersion
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def derive_arm_arch(isa: list[str] | None, metadata: dict[str, str] | None) -> str | None:
|
|
22
|
+
"""Classify an Arm intrinsic as A32/A64/BOTH from its supported_architectures.
|
|
23
|
+
|
|
24
|
+
Rules (matching the Part A preset design):
|
|
25
|
+
* contains A64 and (A32 or v7 or MVE) -> "BOTH"
|
|
26
|
+
* contains A64 only -> "A64"
|
|
27
|
+
* contains A32 or v7 or MVE only -> "A32"
|
|
28
|
+
* non-Arm rows or missing metadata -> None
|
|
29
|
+
"""
|
|
30
|
+
supported = str((metadata or {}).get("supported_architectures") or "").strip()
|
|
31
|
+
if not supported:
|
|
32
|
+
# MVE-only Arm intrinsics may not carry supported_architectures;
|
|
33
|
+
# infer A32 from the ISA list in that case.
|
|
34
|
+
if isa and any(tok.upper() == "MVE" for tok in isa):
|
|
35
|
+
return "A32"
|
|
36
|
+
return None
|
|
37
|
+
upper = supported.upper()
|
|
38
|
+
has_a64 = "A64" in upper
|
|
39
|
+
has_a32 = ("A32" in upper) or ("V7" in upper) or ("MVE" in upper)
|
|
40
|
+
if has_a64 and has_a32:
|
|
41
|
+
return "BOTH"
|
|
42
|
+
if has_a64:
|
|
43
|
+
return "A64"
|
|
44
|
+
if has_a32:
|
|
45
|
+
return "A32"
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
PACKAGE_ROOT = Path(__file__).resolve().parent
|
|
50
|
+
REPO_ROOT = PACKAGE_ROOT.parents[1]
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _default_data_dir() -> Path:
|
|
54
|
+
"""Return the platform-appropriate data directory for simdref.
|
|
55
|
+
|
|
56
|
+
Uses repo-relative paths for editable/dev installs, and a
|
|
57
|
+
platform-appropriate user data directory for wheel installs.
|
|
58
|
+
"""
|
|
59
|
+
# Dev install: pyproject.toml next to src/simdref/
|
|
60
|
+
if (REPO_ROOT / "pyproject.toml").exists() and (REPO_ROOT / "src" / "simdref").is_dir():
|
|
61
|
+
return REPO_ROOT / "data" / "derived"
|
|
62
|
+
|
|
63
|
+
# Installed: use platform-appropriate data dir
|
|
64
|
+
if sys.platform == "win32":
|
|
65
|
+
base = Path(os.environ.get("LOCALAPPDATA", Path.home() / "AppData" / "Local"))
|
|
66
|
+
elif sys.platform == "darwin":
|
|
67
|
+
base = Path.home() / "Library" / "Application Support"
|
|
68
|
+
else:
|
|
69
|
+
base = Path(os.environ.get("XDG_DATA_HOME", Path.home() / ".local" / "share"))
|
|
70
|
+
return base / "simdref"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
DATA_DIR = _default_data_dir()
|
|
74
|
+
_is_dev_install = DATA_DIR == REPO_ROOT / "data" / "derived"
|
|
75
|
+
|
|
76
|
+
if _is_dev_install:
|
|
77
|
+
WEB_DIR = REPO_ROOT / "web"
|
|
78
|
+
DEFAULT_MAN_DIR = REPO_ROOT / "share" / "man"
|
|
79
|
+
else:
|
|
80
|
+
WEB_DIR = DATA_DIR / "web"
|
|
81
|
+
DEFAULT_MAN_DIR = DATA_DIR / "man"
|
|
82
|
+
|
|
83
|
+
CATALOG_PATH = DATA_DIR / "catalog.msgpack"
|
|
84
|
+
SQLITE_PATH = DATA_DIR / "catalog.db"
|
|
85
|
+
SQLITE_SCHEMA_VERSION = "11"
|
|
86
|
+
FTS_TOKEN_RE = re.compile(r"[A-Za-z0-9]+")
|
|
87
|
+
SQLITE_INSERT_BATCH_SIZE = 512
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def ensure_dir(path: Path) -> None:
|
|
91
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def load_catalog(path: Path = CATALOG_PATH) -> Catalog:
|
|
95
|
+
payload = msgpack.unpackb(path.read_bytes(), raw=False)
|
|
96
|
+
return Catalog.from_dict(payload)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def save_catalog(catalog: Catalog, path: Path = CATALOG_PATH) -> None:
|
|
100
|
+
ensure_dir(path.parent)
|
|
101
|
+
packer = msgpack.Packer(use_bin_type=True)
|
|
102
|
+
with path.open("wb") as fh:
|
|
103
|
+
fh.write(packer.pack_map_header(4))
|
|
104
|
+
fh.write(packer.pack("intrinsics"))
|
|
105
|
+
fh.write(packer.pack_array_header(len(catalog.intrinsics)))
|
|
106
|
+
for record in catalog.intrinsics:
|
|
107
|
+
fh.write(packer.pack(record.to_dict()))
|
|
108
|
+
fh.write(packer.pack("instructions"))
|
|
109
|
+
fh.write(packer.pack_array_header(len(catalog.instructions)))
|
|
110
|
+
for record in catalog.instructions:
|
|
111
|
+
fh.write(packer.pack(record.to_dict()))
|
|
112
|
+
fh.write(packer.pack("sources"))
|
|
113
|
+
fh.write(packer.pack_array_header(len(catalog.sources)))
|
|
114
|
+
for source in catalog.sources:
|
|
115
|
+
fh.write(packer.pack(source.to_dict()))
|
|
116
|
+
fh.write(packer.pack("generated_at"))
|
|
117
|
+
fh.write(packer.pack(catalog.generated_at))
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def open_db(path: Path = SQLITE_PATH) -> sqlite3.Connection:
|
|
121
|
+
conn = sqlite3.connect(path)
|
|
122
|
+
conn.row_factory = sqlite3.Row
|
|
123
|
+
return conn
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def sqlite_schema_is_current(path: Path = SQLITE_PATH) -> bool:
|
|
127
|
+
if not path.exists():
|
|
128
|
+
return False
|
|
129
|
+
conn = sqlite3.connect(path)
|
|
130
|
+
try:
|
|
131
|
+
meta = conn.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='meta'").fetchone()
|
|
132
|
+
if meta is None:
|
|
133
|
+
return False
|
|
134
|
+
row = conn.execute("SELECT value FROM meta WHERE key = 'schema_version'").fetchone()
|
|
135
|
+
if row is None or row[0] != SQLITE_SCHEMA_VERSION:
|
|
136
|
+
return False
|
|
137
|
+
expected_columns = {"id", "name", "architecture", "signature", "description", "header", "isa", "category", "subcategory", "arm_arch", "payload"}
|
|
138
|
+
actual_columns = {item[1] for item in conn.execute("PRAGMA table_info(intrinsics_data)").fetchall()}
|
|
139
|
+
if expected_columns != actual_columns:
|
|
140
|
+
return False
|
|
141
|
+
expected_instruction_columns = {"db_key", "key", "architecture", "mnemonic", "form", "summary", "isa", "category", "payload"}
|
|
142
|
+
actual_instruction_columns = {item[1] for item in conn.execute("PRAGMA table_info(instructions_data)").fetchall()}
|
|
143
|
+
if expected_instruction_columns != actual_instruction_columns:
|
|
144
|
+
return False
|
|
145
|
+
instr_indexes = {item[1] for item in conn.execute("PRAGMA index_list(instructions_data)").fetchall()}
|
|
146
|
+
if "idx_instruction_category" not in instr_indexes:
|
|
147
|
+
return False
|
|
148
|
+
intr_indexes = {item[1] for item in conn.execute("PRAGMA index_list(intrinsics_data)").fetchall()}
|
|
149
|
+
return "idx_intrinsic_arm_arch" in intr_indexes
|
|
150
|
+
except sqlite3.Error:
|
|
151
|
+
return False
|
|
152
|
+
finally:
|
|
153
|
+
conn.close()
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
_ALPHA_NUM_SPLIT = re.compile(r"[a-zA-Z]+|[0-9]+")
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _tokenize_name(name: str) -> str:
|
|
160
|
+
"""Split alpha/numeric boundaries for better FTS matching.
|
|
161
|
+
|
|
162
|
+
_mm256_add_epi32 → mm 256 add epi 32
|
|
163
|
+
VADDPS (YMM, YMM, YMM) → vaddps ymm ymm ymm
|
|
164
|
+
"""
|
|
165
|
+
return " ".join(_ALPHA_NUM_SPLIT.findall(name)).lower()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _batched(items, size: int = SQLITE_INSERT_BATCH_SIZE):
|
|
169
|
+
iterator = iter(items)
|
|
170
|
+
while True:
|
|
171
|
+
batch = list(islice(iterator, size))
|
|
172
|
+
if not batch:
|
|
173
|
+
break
|
|
174
|
+
yield batch
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def build_sqlite(catalog: Catalog, path: Path = SQLITE_PATH) -> None:
|
|
178
|
+
ensure_dir(path.parent)
|
|
179
|
+
if path.exists():
|
|
180
|
+
path.unlink()
|
|
181
|
+
conn = sqlite3.connect(path)
|
|
182
|
+
cur = conn.cursor()
|
|
183
|
+
cur.executescript(
|
|
184
|
+
"""
|
|
185
|
+
PRAGMA journal_mode=WAL;
|
|
186
|
+
CREATE TABLE meta (
|
|
187
|
+
key TEXT PRIMARY KEY,
|
|
188
|
+
value TEXT NOT NULL
|
|
189
|
+
);
|
|
190
|
+
CREATE TABLE sources (
|
|
191
|
+
source TEXT PRIMARY KEY,
|
|
192
|
+
payload BLOB NOT NULL
|
|
193
|
+
);
|
|
194
|
+
CREATE TABLE intrinsics_data (
|
|
195
|
+
id INTEGER PRIMARY KEY,
|
|
196
|
+
name TEXT NOT NULL COLLATE NOCASE,
|
|
197
|
+
architecture TEXT NOT NULL,
|
|
198
|
+
signature TEXT NOT NULL,
|
|
199
|
+
description TEXT NOT NULL,
|
|
200
|
+
header TEXT NOT NULL,
|
|
201
|
+
isa TEXT NOT NULL,
|
|
202
|
+
category TEXT NOT NULL,
|
|
203
|
+
subcategory TEXT NOT NULL DEFAULT '',
|
|
204
|
+
arm_arch TEXT,
|
|
205
|
+
payload BLOB NOT NULL
|
|
206
|
+
);
|
|
207
|
+
CREATE INDEX idx_intrinsic_name ON intrinsics_data (name);
|
|
208
|
+
CREATE INDEX idx_intrinsic_arm_arch ON intrinsics_data (arm_arch);
|
|
209
|
+
CREATE TABLE instructions_data (
|
|
210
|
+
db_key TEXT PRIMARY KEY COLLATE NOCASE,
|
|
211
|
+
key TEXT NOT NULL COLLATE NOCASE,
|
|
212
|
+
architecture TEXT NOT NULL,
|
|
213
|
+
mnemonic TEXT NOT NULL COLLATE NOCASE,
|
|
214
|
+
form TEXT NOT NULL,
|
|
215
|
+
summary TEXT NOT NULL,
|
|
216
|
+
isa TEXT NOT NULL,
|
|
217
|
+
category TEXT NOT NULL DEFAULT '',
|
|
218
|
+
payload BLOB NOT NULL
|
|
219
|
+
);
|
|
220
|
+
CREATE INDEX idx_instruction_key ON instructions_data (key);
|
|
221
|
+
CREATE INDEX idx_instruction_mnemonic ON instructions_data (mnemonic);
|
|
222
|
+
CREATE INDEX idx_instruction_category ON instructions_data (category);
|
|
223
|
+
CREATE VIRTUAL TABLE intrinsics_fts USING fts5(name, signature, description, header, isa, category, instructions, notes, aliases, summary, name_tokens);
|
|
224
|
+
CREATE VIRTUAL TABLE instructions_fts USING fts5(key, mnemonic, form, summary, isa, linked_intrinsics, aliases, key_tokens);
|
|
225
|
+
"""
|
|
226
|
+
)
|
|
227
|
+
cur.execute("INSERT INTO meta VALUES (?, ?)", ("schema_version", SQLITE_SCHEMA_VERSION))
|
|
228
|
+
cur.execute("INSERT INTO meta VALUES (?, ?)", ("generated_at", catalog.generated_at))
|
|
229
|
+
|
|
230
|
+
# Sources
|
|
231
|
+
source_rows = (
|
|
232
|
+
(source.source, msgpack.packb(source.to_dict(), use_bin_type=True))
|
|
233
|
+
for source in catalog.sources
|
|
234
|
+
)
|
|
235
|
+
for batch in _batched(source_rows):
|
|
236
|
+
cur.executemany("INSERT INTO sources VALUES (?, ?)", batch)
|
|
237
|
+
|
|
238
|
+
# Build a mnemonic -> summary lookup from instructions for fast access
|
|
239
|
+
_instr_summary: dict[str, str] = {}
|
|
240
|
+
for irec in catalog.instructions:
|
|
241
|
+
if irec.mnemonic and irec.summary and irec.mnemonic not in _instr_summary:
|
|
242
|
+
_instr_summary[irec.mnemonic] = irec.summary
|
|
243
|
+
|
|
244
|
+
# Intrinsics data + FTS
|
|
245
|
+
intrinsics_data_batch = []
|
|
246
|
+
intrinsics_fts_batch = []
|
|
247
|
+
for record in catalog.intrinsics:
|
|
248
|
+
payload = msgpack.packb(record.to_dict(), use_bin_type=True)
|
|
249
|
+
intrinsics_data_batch.append((
|
|
250
|
+
record.name,
|
|
251
|
+
record.architecture,
|
|
252
|
+
record.signature,
|
|
253
|
+
record.description,
|
|
254
|
+
record.header,
|
|
255
|
+
" ".join(record.isa),
|
|
256
|
+
record.category,
|
|
257
|
+
record.subcategory,
|
|
258
|
+
derive_arm_arch(record.isa, record.metadata),
|
|
259
|
+
payload,
|
|
260
|
+
))
|
|
261
|
+
instr_summary = ""
|
|
262
|
+
if record.instructions:
|
|
263
|
+
mnemonic = record.instructions[0].split("(")[0].split()[0].strip()
|
|
264
|
+
instr_summary = _instr_summary.get(mnemonic, "")
|
|
265
|
+
if not instr_summary and record.description:
|
|
266
|
+
instr_summary = record.description.split(".")[0] + "."
|
|
267
|
+
intrinsics_fts_batch.append((
|
|
268
|
+
record.name,
|
|
269
|
+
record.signature,
|
|
270
|
+
record.description,
|
|
271
|
+
record.header,
|
|
272
|
+
" ".join(record.isa),
|
|
273
|
+
record.category,
|
|
274
|
+
" ".join(record.instructions),
|
|
275
|
+
" ".join(record.notes),
|
|
276
|
+
" ".join(record.aliases),
|
|
277
|
+
instr_summary,
|
|
278
|
+
_tokenize_name(record.name),
|
|
279
|
+
))
|
|
280
|
+
if len(intrinsics_data_batch) >= SQLITE_INSERT_BATCH_SIZE:
|
|
281
|
+
cur.executemany(
|
|
282
|
+
"INSERT INTO intrinsics_data (name, architecture, signature, description, header, isa, category, subcategory, arm_arch, payload) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
283
|
+
intrinsics_data_batch,
|
|
284
|
+
)
|
|
285
|
+
cur.executemany(
|
|
286
|
+
"INSERT INTO intrinsics_fts VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
287
|
+
intrinsics_fts_batch,
|
|
288
|
+
)
|
|
289
|
+
intrinsics_data_batch.clear()
|
|
290
|
+
intrinsics_fts_batch.clear()
|
|
291
|
+
if intrinsics_data_batch:
|
|
292
|
+
cur.executemany(
|
|
293
|
+
"INSERT INTO intrinsics_data (name, architecture, signature, description, header, isa, category, subcategory, arm_arch, payload) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
294
|
+
intrinsics_data_batch,
|
|
295
|
+
)
|
|
296
|
+
cur.executemany(
|
|
297
|
+
"INSERT INTO intrinsics_fts VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
298
|
+
intrinsics_fts_batch,
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
# Instructions data + FTS
|
|
302
|
+
instructions_data_batch = []
|
|
303
|
+
instructions_fts_batch = []
|
|
304
|
+
for record in catalog.instructions:
|
|
305
|
+
payload = msgpack.packb(record.to_dict(), use_bin_type=True)
|
|
306
|
+
instructions_data_batch.append((
|
|
307
|
+
record.db_key,
|
|
308
|
+
record.key,
|
|
309
|
+
record.architecture,
|
|
310
|
+
record.mnemonic,
|
|
311
|
+
record.form,
|
|
312
|
+
record.summary,
|
|
313
|
+
" ".join(record.isa),
|
|
314
|
+
record.metadata.get("category", "") if isinstance(record.metadata, dict) else "",
|
|
315
|
+
payload,
|
|
316
|
+
))
|
|
317
|
+
instructions_fts_batch.append((
|
|
318
|
+
record.key,
|
|
319
|
+
record.mnemonic,
|
|
320
|
+
record.form,
|
|
321
|
+
record.summary,
|
|
322
|
+
" ".join(record.isa),
|
|
323
|
+
" ".join(record.linked_intrinsics),
|
|
324
|
+
" ".join(record.aliases),
|
|
325
|
+
_tokenize_name(record.key),
|
|
326
|
+
))
|
|
327
|
+
if len(instructions_data_batch) >= SQLITE_INSERT_BATCH_SIZE:
|
|
328
|
+
cur.executemany(
|
|
329
|
+
"INSERT INTO instructions_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
330
|
+
instructions_data_batch,
|
|
331
|
+
)
|
|
332
|
+
cur.executemany(
|
|
333
|
+
"INSERT INTO instructions_fts VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
334
|
+
instructions_fts_batch,
|
|
335
|
+
)
|
|
336
|
+
instructions_data_batch.clear()
|
|
337
|
+
instructions_fts_batch.clear()
|
|
338
|
+
if instructions_data_batch:
|
|
339
|
+
cur.executemany(
|
|
340
|
+
"INSERT INTO instructions_data VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
|
341
|
+
instructions_data_batch,
|
|
342
|
+
)
|
|
343
|
+
cur.executemany(
|
|
344
|
+
"INSERT INTO instructions_fts VALUES (?, ?, ?, ?, ?, ?, ?, ?)",
|
|
345
|
+
instructions_fts_batch,
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
conn.commit()
|
|
349
|
+
conn.close()
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def load_sources_from_db(conn: sqlite3.Connection) -> list[SourceVersion]:
|
|
353
|
+
rows = conn.execute("SELECT payload FROM sources ORDER BY source").fetchall()
|
|
354
|
+
return [SourceVersion(**msgpack.unpackb(row["payload"], raw=False)) for row in rows]
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def generated_at_from_db(conn: sqlite3.Connection) -> str:
|
|
358
|
+
row = conn.execute("SELECT value FROM meta WHERE key = 'generated_at'").fetchone()
|
|
359
|
+
return row["value"] if row else ""
|
|
360
|
+
|
|
361
|
+
|
|
362
|
+
def load_intrinsic_from_db(conn: sqlite3.Connection, name: str) -> IntrinsicRecord | None:
|
|
363
|
+
row = conn.execute(
|
|
364
|
+
"SELECT payload FROM intrinsics_data WHERE name = ? ORDER BY id LIMIT 1",
|
|
365
|
+
(name,),
|
|
366
|
+
).fetchone()
|
|
367
|
+
if not row:
|
|
368
|
+
return None
|
|
369
|
+
return IntrinsicRecord(**msgpack.unpackb(row["payload"], raw=False))
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def load_instruction_from_db(conn: sqlite3.Connection, key: str) -> InstructionRecord | None:
|
|
373
|
+
row = conn.execute(
|
|
374
|
+
"""
|
|
375
|
+
SELECT payload
|
|
376
|
+
FROM instructions_data
|
|
377
|
+
WHERE db_key = ? OR key = ?
|
|
378
|
+
ORDER BY CASE WHEN db_key = ? THEN 0 ELSE 1 END, architecture, key
|
|
379
|
+
LIMIT 1
|
|
380
|
+
""",
|
|
381
|
+
(key, key, key),
|
|
382
|
+
).fetchone()
|
|
383
|
+
if not row:
|
|
384
|
+
return None
|
|
385
|
+
return InstructionRecord(**msgpack.unpackb(row["payload"], raw=False))
|
|
386
|
+
|
|
387
|
+
|
|
388
|
+
def load_instructions_by_mnemonic_from_db(conn: sqlite3.Connection, mnemonic: str) -> list[InstructionRecord]:
|
|
389
|
+
rows = conn.execute("SELECT payload FROM instructions_data WHERE mnemonic = ? ORDER BY architecture, key", (mnemonic,)).fetchall()
|
|
390
|
+
return [InstructionRecord(**msgpack.unpackb(row["payload"], raw=False)) for row in rows]
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def load_instructions_by_mnemonic_prefix_from_db(conn: sqlite3.Connection, prefix: str, limit: int = 400) -> list[InstructionRecord]:
|
|
394
|
+
rows = conn.execute(
|
|
395
|
+
"""
|
|
396
|
+
SELECT payload
|
|
397
|
+
FROM instructions_data
|
|
398
|
+
WHERE mnemonic LIKE ? || '%'
|
|
399
|
+
ORDER BY mnemonic, architecture, key
|
|
400
|
+
LIMIT ?
|
|
401
|
+
""",
|
|
402
|
+
(prefix, limit),
|
|
403
|
+
).fetchall()
|
|
404
|
+
return [InstructionRecord(**msgpack.unpackb(row["payload"], raw=False)) for row in rows]
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
def _fts_match_query(query: str) -> str:
|
|
408
|
+
tokens = [token.casefold() for token in FTS_TOKEN_RE.findall(query)]
|
|
409
|
+
return " AND ".join(f'"{token}"*' for token in tokens if token)
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _append_filter_clause(
|
|
413
|
+
base_sql: str,
|
|
414
|
+
table: str,
|
|
415
|
+
filter_spec,
|
|
416
|
+
enabled_families,
|
|
417
|
+
enabled_categories,
|
|
418
|
+
binds: list,
|
|
419
|
+
match_marker: str,
|
|
420
|
+
enabled_arm_arch=None,
|
|
421
|
+
) -> str:
|
|
422
|
+
"""Splice a FilterSpec WHERE fragment into the SQL query after the
|
|
423
|
+
FTS MATCH placeholder, so bind ordering stays correct.
|
|
424
|
+
|
|
425
|
+
``match_marker`` must be the exact string that appears immediately after
|
|
426
|
+
the MATCH ``?`` placeholder in ``base_sql`` (e.g. a newline-preserving
|
|
427
|
+
pattern) — the helper inserts ``AND <clause>`` just after it.
|
|
428
|
+
"""
|
|
429
|
+
if filter_spec is None:
|
|
430
|
+
return base_sql
|
|
431
|
+
clause, extra_binds = filter_spec.sql_predicate(
|
|
432
|
+
table,
|
|
433
|
+
enabled_families=enabled_families,
|
|
434
|
+
enabled_categories=enabled_categories,
|
|
435
|
+
enabled_arm_arch=enabled_arm_arch,
|
|
436
|
+
)
|
|
437
|
+
if not clause:
|
|
438
|
+
return base_sql
|
|
439
|
+
binds.extend(extra_binds)
|
|
440
|
+
if match_marker not in base_sql:
|
|
441
|
+
return base_sql
|
|
442
|
+
return base_sql.replace(match_marker, f"{match_marker} AND {clause} ", 1)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
def search_intrinsic_candidates_from_db(
|
|
446
|
+
conn: sqlite3.Connection,
|
|
447
|
+
query: str,
|
|
448
|
+
limit: int = 200,
|
|
449
|
+
*,
|
|
450
|
+
filter_spec=None,
|
|
451
|
+
enabled_families=None,
|
|
452
|
+
enabled_categories=None,
|
|
453
|
+
enabled_arm_arch=None,
|
|
454
|
+
) -> list[IntrinsicRecord]:
|
|
455
|
+
match_query = _fts_match_query(query)
|
|
456
|
+
if not match_query:
|
|
457
|
+
return []
|
|
458
|
+
binds: list = [match_query]
|
|
459
|
+
sql = """
|
|
460
|
+
SELECT intrinsics_data.payload
|
|
461
|
+
FROM intrinsics_fts
|
|
462
|
+
JOIN intrinsics_data ON intrinsics_data.id = intrinsics_fts.rowid
|
|
463
|
+
WHERE intrinsics_fts MATCH ?
|
|
464
|
+
ORDER BY bm25(intrinsics_fts), length(intrinsics_data.name), intrinsics_data.name
|
|
465
|
+
LIMIT ?
|
|
466
|
+
"""
|
|
467
|
+
sql = _append_filter_clause(
|
|
468
|
+
sql, "intrinsics_data", filter_spec, enabled_families, enabled_categories, binds,
|
|
469
|
+
match_marker="intrinsics_fts MATCH ?",
|
|
470
|
+
enabled_arm_arch=enabled_arm_arch,
|
|
471
|
+
)
|
|
472
|
+
binds.append(limit)
|
|
473
|
+
rows = conn.execute(sql, binds).fetchall()
|
|
474
|
+
return [IntrinsicRecord(**msgpack.unpackb(row["payload"], raw=False)) for row in rows]
|
|
475
|
+
|
|
476
|
+
|
|
477
|
+
def search_instruction_candidates_from_db(
|
|
478
|
+
conn: sqlite3.Connection,
|
|
479
|
+
query: str,
|
|
480
|
+
limit: int = 200,
|
|
481
|
+
*,
|
|
482
|
+
filter_spec=None,
|
|
483
|
+
enabled_families=None,
|
|
484
|
+
enabled_categories=None,
|
|
485
|
+
) -> list[InstructionRecord]:
|
|
486
|
+
match_query = _fts_match_query(query)
|
|
487
|
+
if not match_query:
|
|
488
|
+
return []
|
|
489
|
+
binds: list = [match_query]
|
|
490
|
+
sql = """
|
|
491
|
+
SELECT instructions_data.payload
|
|
492
|
+
FROM instructions_fts
|
|
493
|
+
JOIN instructions_data ON instructions_data.rowid = instructions_fts.rowid
|
|
494
|
+
WHERE instructions_fts MATCH ?
|
|
495
|
+
ORDER BY bm25(instructions_fts), length(instructions_data.key), instructions_data.key
|
|
496
|
+
LIMIT ?
|
|
497
|
+
"""
|
|
498
|
+
sql = _append_filter_clause(
|
|
499
|
+
sql, "instructions_data", filter_spec, enabled_families, enabled_categories, binds,
|
|
500
|
+
match_marker="instructions_fts MATCH ?",
|
|
501
|
+
)
|
|
502
|
+
binds.append(limit)
|
|
503
|
+
rows = conn.execute(sql, binds).fetchall()
|
|
504
|
+
return [InstructionRecord(**msgpack.unpackb(row["payload"], raw=False)) for row in rows]
|
|
File without changes
|