simdref 0.0.4__tar.gz → 0.0.5__tar.gz
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-0.0.4/src/simdref.egg-info → simdref-0.0.5}/PKG-INFO +4 -3
- {simdref-0.0.4 → simdref-0.0.5}/README.md +3 -2
- {simdref-0.0.4 → simdref-0.0.5}/pyproject.toml +1 -1
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/annotate.py +120 -33
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/cli.py +219 -57
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/manpages.py +13 -15
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/perf.py +46 -7
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/hotloop.py +70 -29
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/orchestrate.py +39 -17
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/storage.py +68 -18
- {simdref-0.0.4 → simdref-0.0.5/src/simdref.egg-info}/PKG-INFO +4 -3
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref.egg-info/SOURCES.txt +2 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_coverage_live.py +2 -1
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_coverage_parity.py +2 -2
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_lsp_web.py +3 -6
- simdref-0.0.5/tests/test_riscv_docs_pages.py +52 -0
- simdref-0.0.5/tests/test_storage_payload.py +56 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_storage_schema.py +3 -3
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_web_e2e.py +3 -2
- {simdref-0.0.4 → simdref-0.0.5}/LICENSE +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/setup.cfg +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/__init__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/__main__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/arm_instructions.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/display.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/filters.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/ingest.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/ingest_catalog.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/ingest_pdf.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/ingest_sources.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/lsp.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/models.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/pdfparse/__init__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/pdfparse/base.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/pdfparse/intel.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/pdfparse/registry.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/pdfparse/types.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/pdfrefs.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/perf.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/perf_sources/__init__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/perf_sources/cores.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/perf_sources/llvm_mca.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/perf_sources/llvm_scheduling.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/perf_sources/merge.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/__init__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/__init__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/exegesis.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/mca.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/uprof.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/vtune.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/adapters/xctrace.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/merge.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/model.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/profile/registry.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/queries.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/riscv.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/search.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/templates/__init__.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/templates/app.js +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/templates/favicon.svg +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/templates/index.html +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/templates/logo.svg +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/templates/style.css +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/tui.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/ui_labels.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref/web.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref.egg-info/dependency_links.txt +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref.egg-info/entry_points.txt +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref.egg-info/requires.txt +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/src/simdref.egg-info/top_level.txt +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_annotate.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_audit_coverage.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_auto_update_on_version_change.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_cli_bare_query.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_cli_bootstrap_progress.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_cli_help.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_cli_llm.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_display.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_filters.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_ingest_sources.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_intel_operation_and_url.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_issue2_fixes.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_models.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_pdfparse.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_perf.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_perf_sources.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_presentation.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_preset_persistence.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_search.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_search_index_js.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_search_pushdown.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_source_kind_filter.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_source_validation.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_tui.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_ui_labels_parity.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_version_matches_pyproject.py +0 -0
- {simdref-0.0.4 → simdref-0.0.5}/tests/test_x86_linking.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: simdref
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, LSP, and static web export.
|
|
5
5
|
Author: Marco
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -39,8 +39,9 @@ Dynamic: license-file
|
|
|
39
39
|
A single searchable reference for SIMD intrinsics and instructions across
|
|
40
40
|
**x86 (Intel + uops.info)**, **Arm (ACLE / AARCHMRS)**, and **RISC-V
|
|
41
41
|
(RVV + unified-db)**. Runs as a CLI, a Textual TUI, an LSP server,
|
|
42
|
-
|
|
43
|
-
|
|
42
|
+
on-demand manpages (`simdref man` — or run `simdref install-manpages`
|
|
43
|
+
so plain `man vpaddd` works), a static web app, and a structured JSON
|
|
44
|
+
interface for LLM skills.
|
|
44
45
|
|
|
45
46
|
[Web App](https://diamondinoia.github.io/simdref/) ·
|
|
46
47
|
[TestPyPI](https://test.pypi.org/project/simdref/) ·
|
|
@@ -7,8 +7,9 @@
|
|
|
7
7
|
A single searchable reference for SIMD intrinsics and instructions across
|
|
8
8
|
**x86 (Intel + uops.info)**, **Arm (ACLE / AARCHMRS)**, and **RISC-V
|
|
9
9
|
(RVV + unified-db)**. Runs as a CLI, a Textual TUI, an LSP server,
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
on-demand manpages (`simdref man` — or run `simdref install-manpages`
|
|
11
|
+
so plain `man vpaddd` works), a static web app, and a structured JSON
|
|
12
|
+
interface for LLM skills.
|
|
12
13
|
|
|
13
14
|
[Web App](https://diamondinoia.github.io/simdref/) ·
|
|
14
15
|
[TestPyPI](https://test.pypi.org/project/simdref/) ·
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "simdref"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.5"
|
|
8
8
|
description = "Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, LSP, and static web export."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -42,23 +42,59 @@ class AsmLine:
|
|
|
42
42
|
source_line: int | None = None
|
|
43
43
|
|
|
44
44
|
|
|
45
|
+
# Instruction head without trailing comment: the '#' comment (and GAS-style
|
|
46
|
+
# annotations) is split off with str.partition — per the regex HOWTO, string
|
|
47
|
+
# methods beat a lazy re group for a fixed single-character cut.
|
|
45
48
|
_INSTR_RE = re.compile(
|
|
46
49
|
r"^(?P<indent>[ \t]*)"
|
|
47
50
|
r"(?P<mnemonic>[A-Za-z][A-Za-z0-9_.]*)"
|
|
48
|
-
r"(?:[ \t]+(?P<operands
|
|
49
|
-
r"(?:[ \t]*(?P<comment>#.*))?$"
|
|
51
|
+
r"(?:[ \t]+(?P<operands>.*))?$"
|
|
50
52
|
)
|
|
51
53
|
_LABEL_RE = re.compile(r"^[ \t]*[A-Za-z_.$][\w.$]*:")
|
|
52
54
|
# objdump -d line shape: optional whitespace, hex VA, ':', hex bytes, mnemonic ops.
|
|
55
|
+
# Byte tokens are 2-8 hex digits: x86 emits one whitespace-separated token per
|
|
56
|
+
# byte ("48 8b 45 f8"), ARM/RISC-V emit the whole encoding as one token
|
|
57
|
+
# ("a9bf7bfd" / "00050513").
|
|
53
58
|
_OBJDUMP_INSTR_RE = re.compile(
|
|
54
59
|
r"^\s*(?P<addr>[0-9a-fA-F]+):\s+"
|
|
55
|
-
r"(?:(?:[0-9a-fA-F]{2}\s+){1,10})?"
|
|
60
|
+
r"(?:(?:[0-9a-fA-F]{2,8}\s+){1,10})?"
|
|
56
61
|
r"(?P<rest>\S.*?)\s*$"
|
|
57
62
|
)
|
|
58
63
|
# objdump -S injects "file:line" comment lines before the instruction block.
|
|
59
64
|
_OBJDUMP_SRC_RE = re.compile(r"^\s*(?P<file>[^ \t/][^:]*):(?P<line>\d+)\s*$")
|
|
60
65
|
|
|
61
66
|
|
|
67
|
+
def _parse_objdump_instr(line: str) -> tuple[int, str] | None:
|
|
68
|
+
"""Match an ``objdump -d`` instruction line: ``<hexaddr>: [bytes] mnemonic ...``.
|
|
69
|
+
|
|
70
|
+
Returns ``(address, rest)`` where ``rest`` starts at the mnemonic, or None
|
|
71
|
+
when the line is not objdump-shaped (e.g. a column-0 numeric local label
|
|
72
|
+
``1:`` does not match).
|
|
73
|
+
"""
|
|
74
|
+
# Cheap str gates before the regex (HOWTO: "use string methods"): objdump
|
|
75
|
+
# never starts an instruction line at column 0 (GAS local labels ``1:``
|
|
76
|
+
# do), addresses start with a hex char, and the mandatory ':' sits within
|
|
77
|
+
# the first 24 columns.
|
|
78
|
+
if not line or line[0] not in " \t":
|
|
79
|
+
return None
|
|
80
|
+
bare = line.lstrip(" \t")
|
|
81
|
+
if not bare or bare[0] not in "0123456789abcdefABCDEF" or ":" not in bare[:24]:
|
|
82
|
+
return None
|
|
83
|
+
obj_m = _OBJDUMP_INSTR_RE.match(line)
|
|
84
|
+
if not obj_m:
|
|
85
|
+
return None
|
|
86
|
+
try:
|
|
87
|
+
return int(obj_m.group("addr"), 16), obj_m.group("rest")
|
|
88
|
+
except ValueError:
|
|
89
|
+
return None
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def _split_comment(text: str) -> tuple[str, str]:
|
|
93
|
+
"""Split AT&T ``#`` trailing comment off; returns (head, comment)."""
|
|
94
|
+
head, sep, comment = text.partition("#")
|
|
95
|
+
return head, (sep + comment if sep else "")
|
|
96
|
+
|
|
97
|
+
|
|
62
98
|
def parse_asm_line(line: str, *, track_positions: bool = False) -> AsmLine:
|
|
63
99
|
stripped = line.rstrip("\n")
|
|
64
100
|
if not stripped.strip():
|
|
@@ -68,17 +104,16 @@ def parse_asm_line(line: str, *, track_positions: bool = False) -> AsmLine:
|
|
|
68
104
|
return AsmLine(LineKind.COMMENT, stripped)
|
|
69
105
|
if bare.startswith("."):
|
|
70
106
|
return AsmLine(LineKind.DIRECTIVE, stripped)
|
|
107
|
+
if bare.startswith("Disassembly of "):
|
|
108
|
+
# objdump section banner: "Disassembly of section .text:"
|
|
109
|
+
return AsmLine(LineKind.COMMENT, stripped)
|
|
71
110
|
|
|
72
|
-
address: int | None = None
|
|
73
111
|
if track_positions:
|
|
74
|
-
|
|
75
|
-
if
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
address = None
|
|
80
|
-
rest = obj_m.group("rest")
|
|
81
|
-
m = _INSTR_RE.match(rest)
|
|
112
|
+
obj = _parse_objdump_instr(stripped)
|
|
113
|
+
if obj is not None:
|
|
114
|
+
address, rest = obj
|
|
115
|
+
head, comment = _split_comment(rest)
|
|
116
|
+
m = _INSTR_RE.match(head)
|
|
82
117
|
if m:
|
|
83
118
|
return AsmLine(
|
|
84
119
|
kind=LineKind.INSTRUCTION,
|
|
@@ -86,7 +121,7 @@ def parse_asm_line(line: str, *, track_positions: bool = False) -> AsmLine:
|
|
|
86
121
|
indent=(stripped[: stripped.find(rest)] if rest in stripped else ""),
|
|
87
122
|
mnemonic=m.group("mnemonic") or "",
|
|
88
123
|
operands=(m.group("operands") or "").strip(),
|
|
89
|
-
trailing_comment=
|
|
124
|
+
trailing_comment=comment.strip(),
|
|
90
125
|
address=address,
|
|
91
126
|
)
|
|
92
127
|
# In objdump mode, any non-matching line is an objdump header,
|
|
@@ -98,7 +133,27 @@ def parse_asm_line(line: str, *, track_positions: bool = False) -> AsmLine:
|
|
|
98
133
|
if _LABEL_RE.match(stripped):
|
|
99
134
|
return AsmLine(LineKind.LABEL, stripped)
|
|
100
135
|
|
|
101
|
-
|
|
136
|
+
# objdump-style input (workflow §1d: `objdump -d ... > file.s`) is a
|
|
137
|
+
# first-class input even without --track-positions: strip the leading
|
|
138
|
+
# " <hexaddr>:\t" (and any raw bytes column) so the mnemonic parses.
|
|
139
|
+
obj = _parse_objdump_instr(stripped)
|
|
140
|
+
if obj is not None:
|
|
141
|
+
address, rest = obj
|
|
142
|
+
head, comment = _split_comment(rest)
|
|
143
|
+
m = _INSTR_RE.match(head)
|
|
144
|
+
if m:
|
|
145
|
+
return AsmLine(
|
|
146
|
+
kind=LineKind.INSTRUCTION,
|
|
147
|
+
raw=stripped,
|
|
148
|
+
indent=(stripped[: stripped.find(rest)] if rest in stripped else ""),
|
|
149
|
+
mnemonic=m.group("mnemonic") or "",
|
|
150
|
+
operands=(m.group("operands") or "").strip(),
|
|
151
|
+
trailing_comment=comment.strip(),
|
|
152
|
+
address=address,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
head, comment = _split_comment(stripped)
|
|
156
|
+
m = _INSTR_RE.match(head)
|
|
102
157
|
if not m:
|
|
103
158
|
return AsmLine(LineKind.COMMENT, stripped)
|
|
104
159
|
return AsmLine(
|
|
@@ -107,8 +162,8 @@ def parse_asm_line(line: str, *, track_positions: bool = False) -> AsmLine:
|
|
|
107
162
|
indent=m.group("indent") or "",
|
|
108
163
|
mnemonic=m.group("mnemonic") or "",
|
|
109
164
|
operands=(m.group("operands") or "").strip(),
|
|
110
|
-
trailing_comment=
|
|
111
|
-
address=
|
|
165
|
+
trailing_comment=comment.strip(),
|
|
166
|
+
address=None,
|
|
112
167
|
)
|
|
113
168
|
|
|
114
169
|
|
|
@@ -614,9 +669,23 @@ def _annotate_instruction(
|
|
|
614
669
|
parsed: AsmLine,
|
|
615
670
|
opts: AnnotateOptions,
|
|
616
671
|
conn: sqlite3.Connection,
|
|
672
|
+
lookup_cache: dict[str, list[InstructionRecord]] | None = None,
|
|
673
|
+
stats: dict[str, int] | None = None,
|
|
617
674
|
) -> tuple[str, dict[str, Any] | None]:
|
|
618
675
|
"""Return the rendered output line and an optional JSON record."""
|
|
619
|
-
|
|
676
|
+
if lookup_cache is not None:
|
|
677
|
+
records = lookup_cache.get(parsed.mnemonic)
|
|
678
|
+
if records is None:
|
|
679
|
+
# Module-level call (not a captured reference) so tests can
|
|
680
|
+
# keep monkeypatching ``annotate.lookup`` with a 2-arg fake.
|
|
681
|
+
records = lookup(parsed.mnemonic, conn)
|
|
682
|
+
lookup_cache[parsed.mnemonic] = records
|
|
683
|
+
else:
|
|
684
|
+
records = lookup(parsed.mnemonic, conn)
|
|
685
|
+
if stats is not None:
|
|
686
|
+
stats["parsed"] = stats.get("parsed", 0) + 1
|
|
687
|
+
if records:
|
|
688
|
+
stats["recognized"] = stats.get("recognized", 0) + 1
|
|
620
689
|
record = pick_record(records, arch=opts.arch, operands=parsed.operands)
|
|
621
690
|
|
|
622
691
|
if record is None:
|
|
@@ -693,34 +762,52 @@ def annotate_stream(
|
|
|
693
762
|
*,
|
|
694
763
|
opts: AnnotateOptions,
|
|
695
764
|
conn: sqlite3.Connection,
|
|
765
|
+
stats: dict[str, int] | None = None,
|
|
696
766
|
) -> Iterator[str]:
|
|
697
|
-
"""Yield annotated lines for each input line (newline-terminated).
|
|
767
|
+
"""Yield annotated lines for each input line (newline-terminated).
|
|
768
|
+
|
|
769
|
+
``stats`` (optional) accumulates parse/recognition counters — keys
|
|
770
|
+
``parsed``, ``recognized``, ``content`` — so callers can warn when the
|
|
771
|
+
input format was not understood (e.g. 0 of N instruction lines found).
|
|
772
|
+
"""
|
|
698
773
|
json_records: list[dict[str, Any]] = []
|
|
699
774
|
collecting_json = opts.fmt == "json"
|
|
700
775
|
pending_src_file: str | None = None
|
|
701
776
|
pending_src_line: int | None = None
|
|
777
|
+
lookup_cache: dict[str, list[InstructionRecord]] = {}
|
|
702
778
|
|
|
703
779
|
for line in lines:
|
|
704
|
-
if opts.track_positions:
|
|
705
|
-
src_m = _OBJDUMP_SRC_RE.match(line.rstrip("\n"))
|
|
706
|
-
if src_m:
|
|
707
|
-
pending_src_file = src_m.group("file").strip()
|
|
708
|
-
try:
|
|
709
|
-
pending_src_line = int(src_m.group("line"))
|
|
710
|
-
except ValueError:
|
|
711
|
-
pending_src_line = None
|
|
712
|
-
if not collecting_json:
|
|
713
|
-
yield line if line.endswith("\n") else line + "\n"
|
|
714
|
-
continue
|
|
715
780
|
parsed = parse_asm_line(line, track_positions=opts.track_positions)
|
|
716
|
-
if opts.track_positions
|
|
717
|
-
parsed.
|
|
718
|
-
|
|
781
|
+
if opts.track_positions:
|
|
782
|
+
if parsed.kind == LineKind.INSTRUCTION:
|
|
783
|
+
parsed.source_file = pending_src_file
|
|
784
|
+
parsed.source_line = pending_src_line
|
|
785
|
+
else:
|
|
786
|
+
# `-S` source interleave: objdump injects "file:line" marker
|
|
787
|
+
# lines immediately before the block they describe. Only
|
|
788
|
+
# non-instruction lines reach the (pricier) marker regex.
|
|
789
|
+
src_m = _OBJDUMP_SRC_RE.match(parsed.raw)
|
|
790
|
+
if src_m:
|
|
791
|
+
pending_src_file = src_m.group("file").strip()
|
|
792
|
+
try:
|
|
793
|
+
pending_src_line = int(src_m.group("line"))
|
|
794
|
+
except ValueError:
|
|
795
|
+
pending_src_line = None
|
|
796
|
+
if not collecting_json:
|
|
797
|
+
yield parsed.raw + "\n"
|
|
798
|
+
continue
|
|
799
|
+
if stats is not None and parsed.kind != LineKind.BLANK:
|
|
800
|
+
# "content" = anything that is not blank, an assembler directive,
|
|
801
|
+
# or a real comment — i.e. instructions, labels, and unrecognised
|
|
802
|
+
# lines the parser rejected (so "recognised 0/N" can warn).
|
|
803
|
+
s = parsed.raw.lstrip()
|
|
804
|
+
if s and not s.startswith((".", "#", "//")):
|
|
805
|
+
stats["content"] = stats.get("content", 0) + 1
|
|
719
806
|
if parsed.kind != LineKind.INSTRUCTION:
|
|
720
807
|
if not collecting_json:
|
|
721
808
|
yield parsed.raw + "\n"
|
|
722
809
|
continue
|
|
723
|
-
out_line, record = _annotate_instruction(parsed, opts, conn)
|
|
810
|
+
out_line, record = _annotate_instruction(parsed, opts, conn, lookup_cache, stats)
|
|
724
811
|
if collecting_json:
|
|
725
812
|
if record is not None:
|
|
726
813
|
json_records.append(record)
|