simdref 0.0.16__tar.gz → 0.0.18__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.16/src/simdref.egg-info → simdref-0.0.18}/PKG-INFO +4 -2
- {simdref-0.0.16 → simdref-0.0.18}/README.md +2 -0
- {simdref-0.0.16 → simdref-0.0.18}/pyproject.toml +2 -2
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/annotate.py +282 -41
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/arm_instructions.py +0 -1
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/asm_identity.py +166 -86
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/asmline.py +106 -7
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/catkey.py +46 -3
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/cli.py +119 -67
- simdref-0.0.18/src/simdref/cuda.py +50 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/display.py +15 -2
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/filters.py +2 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/ingest.py +2 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/ingest_catalog.py +14 -1
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/ingest_sources.py +50 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/lsp.py +214 -29
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/manpages.py +20 -8
- simdref-0.0.18/src/simdref/ptx.py +759 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/riscv.py +0 -1
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/storage.py +10 -0
- {simdref-0.0.16 → simdref-0.0.18/src/simdref.egg-info}/PKG-INFO +4 -2
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref.egg-info/SOURCES.txt +15 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref.egg-info/requires.txt +1 -1
- simdref-0.0.18/tests/test_annotate_scans.py +52 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_asm_wire.py +73 -4
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_auto_update_on_version_change.py +1 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_catkey.py +4 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_cli_bare_query.py +34 -0
- simdref-0.0.18/tests/test_cuda_declaration_parser.py +60 -0
- simdref-0.0.18/tests/test_cuda_intrinsics.py +109 -0
- simdref-0.0.18/tests/test_cuda_td_expansion.py +108 -0
- simdref-0.0.18/tests/test_cuda_wiring.py +81 -0
- simdref-0.0.18/tests/test_fetch_retry.py +175 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_ingest_sources.py +20 -0
- simdref-0.0.18/tests/test_issue_65_branch_target.py +91 -0
- simdref-0.0.18/tests/test_issue_65_foreign_arch.py +236 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_identity.py +40 -15
- simdref-0.0.18/tests/test_issue_65_m5.py +22 -0
- simdref-0.0.18/tests/test_issue_65_sme_za.py +128 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_arm_form_pick.py +3 -3
- simdref-0.0.18/tests/test_lsp_cuda.py +119 -0
- simdref-0.0.18/tests/test_lsp_ptx.py +311 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_lsp_stdio.py +224 -16
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_presentation.py +25 -0
- simdref-0.0.18/tests/test_ptx.py +253 -0
- {simdref-0.0.16 → simdref-0.0.18}/LICENSE +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/setup.cfg +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/__init__.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/__main__.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/export.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/htmltext.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/ingest_pdf.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/models.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/operands.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/pdfparse/__init__.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/pdfparse/base.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/pdfparse/intel.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/pdfparse/registry.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/pdfparse/types.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/pdfrefs.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/__init__.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/amd_swopt.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/arm_swopt.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/cores.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/instlatx64.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/intel_guide.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/intel_optref.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/llvm_mca.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/llvm_scheduling.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/perf_sources/merge.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/queries.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/regtable.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/search.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/tablecols.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/tui.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref/ui_labels.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref.egg-info/dependency_links.txt +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref.egg-info/entry_points.txt +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/src/simdref.egg-info/top_level.txt +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_a1_sdm_parser.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_a2_perf_rule.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_a4_arm_pdfrefs.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_a5_offline_aarchmrs.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_amd_match_llvm_mc.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_annotate.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_annotate_positions.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_arrangement_tail.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_asm_identity.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_asmline.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_audit_coverage.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b1_latency_pairs.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b2_metadata.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b3_sdm_forms.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b4_descriptions_provenance.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b5_linking.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b5_perf_family.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_b5_sme.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_c0_perf_priority.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_cli_bootstrap_progress.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_cli_help.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_cli_llm.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_coverage_live.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_coverage_parity.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_display.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_display_scanners.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_export.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_export_viz.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_filters.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_fixui_display.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_htmltext.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_ingest_enc_fields.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_int_arm_isa_label.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_int_core_ids.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_int_ingest_registration.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_int_sdm_family_fallback.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_intel_operation_and_url.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_isa_detect_llvm_mc.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue2_fixes.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_51.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_52.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_53_54.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_55.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_56_57a.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_57_cpi.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_60_c1.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_60_contract.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_60_crossarch.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_60_matcher.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_arm_alias.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_compressed.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_m1.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_perfdata.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_text.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issue_65_timing.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issues_23_24_25.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_issues_51_57_join.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_arm_postindex.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_att_movx.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_autoupdate.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_catalog_gaps.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_ingest_isa_merge.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_intel_size.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_perf_join.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_leftover_riscv_shared.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_lsp_web.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_models.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_no_catalog_row.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_objdump_listings.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_operands.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_pdfparse.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf_source_amd_swopt.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf_source_arm_swopt.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf_source_instlatx64.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf_source_intel_guide.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf_source_intel_optref.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_perf_sources.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_preset_persistence.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_regtable.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_release_tag_pin.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_riscv_docs_pages.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_search.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_search_pushdown.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_shared_source_helpers.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_source_kind_filter.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_source_validation.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_storage_alnum.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_storage_payload.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_storage_schema.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_tablecols.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_tui.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_ui_labels_parity.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_update_build_stamp.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_vendor_riscv_categories.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_vendor_riscv_sources.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_version_matches_pyproject.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/tests/test_x86_key_widths.py +0 -0
- {simdref-0.0.16 → simdref-0.0.18}/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.18
|
|
4
4
|
Summary: Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, and LSP.
|
|
5
5
|
Author: Marco
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -25,7 +25,7 @@ Requires-Dist: msgpack<2,>=1.2.4
|
|
|
25
25
|
Requires-Dist: PyYAML<7,>=6.0.3
|
|
26
26
|
Requires-Dist: rapidfuzz<4,>=3.14.6
|
|
27
27
|
Requires-Dist: rich<16,>=15.0.0
|
|
28
|
-
Requires-Dist: simdref-llvm-mc==23.1.4; (sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64")) or (sys_platform == "darwin" and (platform_machine == "arm64" or platform_machine == "x86_64")) or (sys_platform == "win32" and platform_machine == "AMD64")
|
|
28
|
+
Requires-Dist: simdref-llvm-mc==23.1.4.post1; (sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64")) or (sys_platform == "darwin" and (platform_machine == "arm64" or platform_machine == "x86_64")) or (sys_platform == "win32" and platform_machine == "AMD64")
|
|
29
29
|
Requires-Dist: textual<9,>=8.2.8
|
|
30
30
|
Requires-Dist: typer<1,>=0.27.3
|
|
31
31
|
Provides-Extra: ingest
|
|
@@ -177,6 +177,8 @@ Dev commands
|
|
|
177
177
|
| Arm AARCHMRS (A64) | Base instruction forms and operand tables | live-only² |
|
|
178
178
|
| riscv-rvv-intrinsic-doc | RVV intrinsics with deterministic instruction refs | 77,285 intrinsics |
|
|
179
179
|
| RISC-V unified-db | RVV instruction forms, ISA tags, description, operation | 3,666 instructions |
|
|
180
|
+
| NVIDIA PTX ISA | Instruction ops, syntax, qualifiers, versions, sm_XX | 478 instructions |
|
|
181
|
+
| CUDA intrinsics | Device intrinsics from clang CUDA headers | 585 intrinsics |
|
|
180
182
|
| Intel Guide perf tables | Per-core latency and throughput | 9,134 rows |
|
|
181
183
|
| Intel Optimization Ref. | Latency and throughput tables (volume 2) | 3,155 rows |
|
|
182
184
|
| InstLatX64 | Measured latency and throughput dumps | 16,235 rows |
|
|
@@ -142,6 +142,8 @@ Dev commands
|
|
|
142
142
|
| Arm AARCHMRS (A64) | Base instruction forms and operand tables | live-only² |
|
|
143
143
|
| riscv-rvv-intrinsic-doc | RVV intrinsics with deterministic instruction refs | 77,285 intrinsics |
|
|
144
144
|
| RISC-V unified-db | RVV instruction forms, ISA tags, description, operation | 3,666 instructions |
|
|
145
|
+
| NVIDIA PTX ISA | Instruction ops, syntax, qualifiers, versions, sm_XX | 478 instructions |
|
|
146
|
+
| CUDA intrinsics | Device intrinsics from clang CUDA headers | 585 intrinsics |
|
|
145
147
|
| Intel Guide perf tables | Per-core latency and throughput | 9,134 rows |
|
|
146
148
|
| Intel Optimization Ref. | Latency and throughput tables (volume 2) | 3,155 rows |
|
|
147
149
|
| InstLatX64 | Measured latency and throughput dumps | 16,235 rows |
|
|
@@ -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.18"
|
|
8
8
|
description = "Searchable SIMD intrinsic and instruction reference with CLI, manpages, TUI, and LSP."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -42,7 +42,7 @@ dependencies = [
|
|
|
42
42
|
# manylinux x86_64 aarch64, macOS arm64 x86_64 and win_amd64, so the
|
|
43
43
|
# marker keeps the dependency off every other platform (musl included),
|
|
44
44
|
# where pip must see no candidate instead of failing to resolve.
|
|
45
|
-
'simdref-llvm-mc==23.1.4; (sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64")) or (sys_platform == "darwin" and (platform_machine == "arm64" or platform_machine == "x86_64")) or (sys_platform == "win32" and platform_machine == "AMD64")',
|
|
45
|
+
'simdref-llvm-mc==23.1.4.post1; (sys_platform == "linux" and (platform_machine == "x86_64" or platform_machine == "aarch64")) or (sys_platform == "darwin" and (platform_machine == "arm64" or platform_machine == "x86_64")) or (sys_platform == "win32" and platform_machine == "AMD64")',
|
|
46
46
|
"textual>=8.2.8,<9",
|
|
47
47
|
"typer>=0.27.3,<1",
|
|
48
48
|
]
|
|
@@ -9,7 +9,6 @@ simdref catalog.
|
|
|
9
9
|
from __future__ import annotations
|
|
10
10
|
|
|
11
11
|
import json
|
|
12
|
-
import re
|
|
13
12
|
import sqlite3
|
|
14
13
|
import statistics
|
|
15
14
|
import string
|
|
@@ -18,9 +17,19 @@ from enum import Enum
|
|
|
18
17
|
from typing import Any, Iterable, Iterator
|
|
19
18
|
|
|
20
19
|
from simdref import perf
|
|
20
|
+
from simdref.arm_instructions import _CONDITION_CODES
|
|
21
21
|
from simdref.asm_identity import isa_by_acceptance
|
|
22
|
-
from simdref.catkey import
|
|
23
|
-
|
|
22
|
+
from simdref.catkey import (
|
|
23
|
+
IMMEDIATE_ACCEPT,
|
|
24
|
+
ISA_FAMILY,
|
|
25
|
+
LITERAL_ACCEPT,
|
|
26
|
+
form_accept,
|
|
27
|
+
isa_family as _isa_family,
|
|
28
|
+
key_names_za_tile,
|
|
29
|
+
names_za_tile,
|
|
30
|
+
operands_of_key,
|
|
31
|
+
)
|
|
32
|
+
from simdref.lsp import NO_CATALOG_ROW, _classify_line, _document_arch, identity_keys
|
|
24
33
|
from simdref.models import InstructionRecord
|
|
25
34
|
from simdref.regtable import REG_TABLE, reg_rows
|
|
26
35
|
from simdref.storage import load_instruction_from_db, load_instructions_by_mnemonic_from_db
|
|
@@ -471,7 +480,8 @@ def _asm_line(stripped: str, obj: _ObjdumpRecord | None, track_positions: bool)
|
|
|
471
480
|
return AsmLine(LineKind.LABEL, stripped)
|
|
472
481
|
|
|
473
482
|
head, comment = _split_comment(stripped)
|
|
474
|
-
|
|
483
|
+
indent = head[: len(head) - len(head.lstrip())]
|
|
484
|
+
m = _instr_head(indent + _strip_prefix_tokens(head))
|
|
475
485
|
if not m:
|
|
476
486
|
return AsmLine(LineKind.COMMENT, stripped)
|
|
477
487
|
return AsmLine(
|
|
@@ -507,11 +517,33 @@ def _lookup_variants(mnemonic: str) -> list[str]:
|
|
|
507
517
|
|
|
508
518
|
|
|
509
519
|
# AArch64 condition-code branches: b.<cond>, no register table covers them.
|
|
510
|
-
|
|
511
|
-
|
|
520
|
+
# The vocabulary is the first spelling of each cond value in the A64 row table
|
|
521
|
+
# (cs/cc alias the hs/lo spellings the llvm-objdump printers print).
|
|
522
|
+
_A64_BCOND_CODES = frozenset(
|
|
523
|
+
dict(reversed([(cond, code) for code, cond in _CONDITION_CODES])).values()
|
|
512
524
|
)
|
|
513
525
|
|
|
514
526
|
|
|
527
|
+
def _bcond_hits(text: str) -> int:
|
|
528
|
+
"""Count b.<cond> branch mnemonics with word boundaries on both sides."""
|
|
529
|
+
count = 0
|
|
530
|
+
i = text.find("b.")
|
|
531
|
+
while i >= 0:
|
|
532
|
+
tail = text[i + 2 : i + 4]
|
|
533
|
+
before = text[i - 1 : i]
|
|
534
|
+
after = text[i + 4 : i + 5]
|
|
535
|
+
if (
|
|
536
|
+
tail in _A64_BCOND_CODES
|
|
537
|
+
and not (before and (before.isalnum() or before == "_"))
|
|
538
|
+
and not (after and (after.isalnum() or after == "_"))
|
|
539
|
+
):
|
|
540
|
+
count += 1
|
|
541
|
+
i = text.find("b.", i + 4)
|
|
542
|
+
else:
|
|
543
|
+
i = text.find("b.", i + 2)
|
|
544
|
+
return count
|
|
545
|
+
|
|
546
|
+
|
|
515
547
|
def _drop_symbol_annotations(text: str) -> str:
|
|
516
548
|
"""Drop objdump symbol annotations ("<x0+0x50>"): a target, never input."""
|
|
517
549
|
out: list[str] = []
|
|
@@ -594,6 +626,42 @@ def _voter_names():
|
|
|
594
626
|
|
|
595
627
|
_EXCL_ARM, _SHARED, _RISCV = _voter_names()
|
|
596
628
|
|
|
629
|
+
# The register families simdref supports: REG_TABLE also carries ARM32 spellings.
|
|
630
|
+
_SUPPORTED_FAMILIES = frozenset({"x86", "a64", "riscv"})
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _foreign_operands(operands: str) -> bool:
|
|
634
|
+
"""True when the operands parse as no supported arch's operands.
|
|
635
|
+
|
|
636
|
+
A ``$``-sigil register (the MIPS/PPC spelling) belongs to no supported
|
|
637
|
+
arch, and a line whose register words all name unsupported families
|
|
638
|
+
(ARM32's r0-r15, ip, pc) carries no supported evidence. A word absent
|
|
639
|
+
from REG_TABLE is a symbol and stays neutral, and one supported-family
|
|
640
|
+
register anywhere clears the whole line (issue #65 B2/B6).
|
|
641
|
+
"""
|
|
642
|
+
unsupported = sigil = False
|
|
643
|
+
i = 0
|
|
644
|
+
n = len(operands)
|
|
645
|
+
while i < n:
|
|
646
|
+
if not (operands[i] == "_" or operands[i].isalnum()):
|
|
647
|
+
i += 1
|
|
648
|
+
continue
|
|
649
|
+
j = i
|
|
650
|
+
while j < n and (operands[j] == "_" or operands[j].isalnum()):
|
|
651
|
+
j += 1
|
|
652
|
+
if i > 0 and operands[i - 1] == "$" and not operands[i].isdigit():
|
|
653
|
+
sigil = True
|
|
654
|
+
else:
|
|
655
|
+
families = {
|
|
656
|
+
family for _klass, _width, family in REG_TABLE.get(operands[i:j].casefold(), ())
|
|
657
|
+
}
|
|
658
|
+
if families & _SUPPORTED_FAMILIES:
|
|
659
|
+
return False
|
|
660
|
+
if families:
|
|
661
|
+
unsupported = True
|
|
662
|
+
i = j
|
|
663
|
+
return sigil or unsupported
|
|
664
|
+
|
|
597
665
|
|
|
598
666
|
def _isa_votes(text: str) -> tuple[int, int, int]:
|
|
599
667
|
"""(excl_arm, excl_riscv, shared) register votes of one instruction text."""
|
|
@@ -608,7 +676,9 @@ def _isa_votes(text: str) -> tuple[int, int, int]:
|
|
|
608
676
|
while j < n and (text[j] == "_" or text[j].isalnum()):
|
|
609
677
|
j += 1
|
|
610
678
|
token = text[i:j]
|
|
611
|
-
|
|
679
|
+
# '%' starts an AT&T register and '$' a sigil register of no
|
|
680
|
+
# supported arch (MIPS/PPC): the name inside never votes (issue #65).
|
|
681
|
+
if i == 0 or text[i - 1] not in "%$":
|
|
612
682
|
if token in _EXCL_ARM:
|
|
613
683
|
excl_arm += 1
|
|
614
684
|
elif token in _SHARED:
|
|
@@ -646,7 +716,7 @@ def detect_isa(lines: Iterable[str]) -> str | None:
|
|
|
646
716
|
# a register (review finding 1).
|
|
647
717
|
text = _drop_symbol_annotations(f"{parsed.mnemonic} {parsed.operands}")
|
|
648
718
|
vote_arm, vote_riscv, vote_shared = _isa_votes(text)
|
|
649
|
-
excl_arm += vote_arm +
|
|
719
|
+
excl_arm += vote_arm + _bcond_hits(text)
|
|
650
720
|
excl_riscv += vote_riscv
|
|
651
721
|
shared += vote_shared
|
|
652
722
|
if excl_arm or excl_riscv:
|
|
@@ -682,10 +752,36 @@ def lookup(
|
|
|
682
752
|
return []
|
|
683
753
|
|
|
684
754
|
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
)
|
|
755
|
+
_SIZE_WORDS = frozenset({"byte", "word", "dword", "qword", "xmmword", "ymmword", "zmmword"})
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
def _has_size_ptr(text: str) -> bool:
|
|
759
|
+
"""True for a size keyword with a trailing "ptr" ("byte ptr [rdx]")."""
|
|
760
|
+
n = len(text)
|
|
761
|
+
i = 0
|
|
762
|
+
while i + 3 <= n:
|
|
763
|
+
if text[i : i + 3].lower() != "ptr":
|
|
764
|
+
i += 1
|
|
765
|
+
continue
|
|
766
|
+
if i + 3 < n and (text[i + 3].isalnum() or text[i + 3] == "_"):
|
|
767
|
+
i += 1
|
|
768
|
+
continue
|
|
769
|
+
j = i
|
|
770
|
+
while j > 0 and text[j - 1].isspace():
|
|
771
|
+
j -= 1
|
|
772
|
+
if j == i:
|
|
773
|
+
i += 1
|
|
774
|
+
continue
|
|
775
|
+
for word in _SIZE_WORDS:
|
|
776
|
+
k = j - len(word)
|
|
777
|
+
if (
|
|
778
|
+
k >= 0
|
|
779
|
+
and text[k:j].lower() == word
|
|
780
|
+
and not (k > 0 and (text[k - 1].isalnum() or text[k - 1] == "_"))
|
|
781
|
+
):
|
|
782
|
+
return True
|
|
783
|
+
i += 1
|
|
784
|
+
return False
|
|
689
785
|
|
|
690
786
|
|
|
691
787
|
def _x86_reg_class(name: str) -> str | None:
|
|
@@ -750,6 +846,26 @@ def _imm_kind(value: int) -> str:
|
|
|
750
846
|
return "I64"
|
|
751
847
|
|
|
752
848
|
|
|
849
|
+
def _is_number(text: str) -> bool:
|
|
850
|
+
"""True for the literal ``-?0x<hex>`` or ``-?<digits>`` the scans matched."""
|
|
851
|
+
body = text[1:] if text[:1] == "-" else text
|
|
852
|
+
if body[:2] == "0x":
|
|
853
|
+
body = body[2:]
|
|
854
|
+
return bool(body) and all(char in string.hexdigits for char in body)
|
|
855
|
+
return bool(body) and body.isdecimal()
|
|
856
|
+
|
|
857
|
+
|
|
858
|
+
def _is_symbol(text: str) -> bool:
|
|
859
|
+
"""True for ``[A-Za-z_.$]`` heads with ``\\w.$@`` tails (a plain symbol)."""
|
|
860
|
+
head = text[:1]
|
|
861
|
+
return (
|
|
862
|
+
bool(head)
|
|
863
|
+
and head.isascii()
|
|
864
|
+
and (head.isalpha() or head in "_.$")
|
|
865
|
+
and all(char.isalnum() or char in "_.$@" for char in text[1:])
|
|
866
|
+
)
|
|
867
|
+
|
|
868
|
+
|
|
753
869
|
def _classify_operand(op: str) -> tuple[str, str | None]:
|
|
754
870
|
"""Return ``(kind, reg_name_lower)`` for one operand.
|
|
755
871
|
|
|
@@ -763,17 +879,22 @@ def _classify_operand(op: str) -> tuple[str, str | None]:
|
|
|
763
879
|
if not s:
|
|
764
880
|
return "?", None
|
|
765
881
|
# objdump branch target: an address followed by a <symbol> annotation.
|
|
766
|
-
|
|
767
|
-
|
|
882
|
+
lt = s.find("<")
|
|
883
|
+
if lt > 0 and s.endswith(">") and ">" not in s[lt + 1 : -1]:
|
|
884
|
+
k = lt
|
|
885
|
+
while k > 0 and s[k - 1].isspace():
|
|
886
|
+
k -= 1
|
|
887
|
+
if k < lt and _is_number(s[:k]):
|
|
888
|
+
return "REL", None
|
|
768
889
|
# AT&T immediate: $... ; Intel immediate: bare number / 0x... / '$'-less literal
|
|
769
|
-
if s.startswith("$") or
|
|
890
|
+
if s.startswith("$") or _is_number(s):
|
|
770
891
|
try:
|
|
771
892
|
return _imm_kind(int(s.lstrip("$"), 0)), None
|
|
772
893
|
except ValueError:
|
|
773
894
|
return "I", None
|
|
774
895
|
# AT&T memory: ``(%reg)``, ``disp(%reg,%idx,scale)``, or plain symbol
|
|
775
896
|
# with a ``(...)`` section; Intel memory: ``[...]`` or explicit size ptr.
|
|
776
|
-
if "(" in s or "[" in s or
|
|
897
|
+
if "(" in s or "[" in s or _has_size_ptr(s):
|
|
777
898
|
return "M", None
|
|
778
899
|
# Pure register?
|
|
779
900
|
name = (s[1:] if s[:1] == "%" else s).casefold()
|
|
@@ -781,7 +902,7 @@ def _classify_operand(op: str) -> tuple[str, str | None]:
|
|
|
781
902
|
if tok is not None or _is_vec_shaped(name):
|
|
782
903
|
return tok or "?", name
|
|
783
904
|
# Plain symbol (branch target) or unknown.
|
|
784
|
-
if
|
|
905
|
+
if _is_symbol(s):
|
|
785
906
|
return "REL", None
|
|
786
907
|
return "?", None
|
|
787
908
|
|
|
@@ -802,9 +923,6 @@ def _operand_shape(operands: str) -> tuple[list[tuple[str, str | None]], bool]:
|
|
|
802
923
|
return slots, aliased
|
|
803
924
|
|
|
804
925
|
|
|
805
|
-
_FORM_OPS_RE = re.compile(r"\(([^)]*)\)\s*$")
|
|
806
|
-
|
|
807
|
-
|
|
808
926
|
def _form_operand_tokens(form_or_key: str) -> list[str]:
|
|
809
927
|
"""Extract the comma-separated operand tokens from a catalog form/key.
|
|
810
928
|
|
|
@@ -812,14 +930,25 @@ def _form_operand_tokens(form_or_key: str) -> list[str]:
|
|
|
812
930
|
``"RET_NEAR"`` returns ``[]``. Tokens are upper-cased.
|
|
813
931
|
"""
|
|
814
932
|
s = str(form_or_key or "")
|
|
815
|
-
|
|
816
|
-
if not
|
|
933
|
+
tail = s.rstrip()
|
|
934
|
+
if not tail or tail[-1] != ")":
|
|
935
|
+
return []
|
|
936
|
+
close = len(tail) - 1
|
|
937
|
+
# The inner text starts at the first "(" after the last ")" before the
|
|
938
|
+
# closing one, so no ")" splits it (the old group matched the same span).
|
|
939
|
+
start = tail.find("(", tail.rfind(")", 0, close) + 1)
|
|
940
|
+
if start < 0:
|
|
817
941
|
return []
|
|
818
|
-
inner =
|
|
942
|
+
inner = tail[start + 1 : close]
|
|
819
943
|
return [t.strip().upper() for t in inner.split(",") if t.strip()]
|
|
820
944
|
|
|
821
945
|
|
|
822
|
-
|
|
946
|
+
def _is_broadcast_token(token: str) -> bool:
|
|
947
|
+
"""True for a form token with a ``_1toN`` tail (``M16_1to8``)."""
|
|
948
|
+
start = len(token)
|
|
949
|
+
while start > 0 and token[start - 1].isdecimal():
|
|
950
|
+
start -= 1
|
|
951
|
+
return start < len(token) and start >= 4 and token[start - 4 : start] == "_1TO"
|
|
823
952
|
|
|
824
953
|
|
|
825
954
|
def _token_matches_slot(form_tok: str, slot_kind: str) -> bool:
|
|
@@ -867,7 +996,7 @@ def _slot_token_score(form_tok: str, slot_kind: str) -> int:
|
|
|
867
996
|
if not _token_matches_slot(ft, sk):
|
|
868
997
|
# Weak near-misses: a broadcast row wants a {1toN} annotation, and a
|
|
869
998
|
# register can ride a memory row (mov r,r against MOV (M32, R32)).
|
|
870
|
-
if sk == "M" and
|
|
999
|
+
if sk == "M" and _is_broadcast_token(ft):
|
|
871
1000
|
return 2
|
|
872
1001
|
if sk.startswith("R") and ("M" + sk[1:]) == ft:
|
|
873
1002
|
return 2
|
|
@@ -966,6 +1095,19 @@ def pick_record(
|
|
|
966
1095
|
# AT&T lists sources first; catalog form tokens are in Intel order.
|
|
967
1096
|
slots = list(reversed(slots))
|
|
968
1097
|
|
|
1098
|
+
# A ZA tile operand names the SME tile rows only, and a line without one
|
|
1099
|
+
# never wants them (issue #65 M11): the rows share the mnemonic and
|
|
1100
|
+
# operand count with the plain SVE/NEON forms, so operand shape alone
|
|
1101
|
+
# picks a timed plain row where the ZA forms carry no timing. Same parity
|
|
1102
|
+
# rule as the "_1to" broadcast filter in asm_identity.
|
|
1103
|
+
za_pair = [
|
|
1104
|
+
record
|
|
1105
|
+
for record in candidates
|
|
1106
|
+
if key_names_za_tile(str(getattr(record, "key", "") or "")) == names_za_tile(operands)
|
|
1107
|
+
]
|
|
1108
|
+
if za_pair:
|
|
1109
|
+
candidates = za_pair
|
|
1110
|
+
|
|
969
1111
|
def score(rec: InstructionRecord) -> tuple[int, int, int, int]:
|
|
970
1112
|
measured = sum(
|
|
971
1113
|
1
|
|
@@ -982,6 +1124,49 @@ def pick_record(
|
|
|
982
1124
|
return max(candidates, key=score)
|
|
983
1125
|
|
|
984
1126
|
|
|
1127
|
+
def _record_takes_line(record: InstructionRecord, operands: str) -> bool:
|
|
1128
|
+
"""Whether the record's key accepts the line's operand classes.
|
|
1129
|
+
|
|
1130
|
+
Classes and accepts come from the catalog: the lsp line classifier and
|
|
1131
|
+
catkey's accept sets. A bare key constrains nothing, a literal key operand
|
|
1132
|
+
(the zero-idiom rows) constrains nothing, and an input the classifier
|
|
1133
|
+
gave no class constrains nothing. Register and memory classes ride each
|
|
1134
|
+
other here exactly like the form pick, so the only hard mismatch is an
|
|
1135
|
+
immediate input against a key operand that accepts no immediate: on an
|
|
1136
|
+
undecided line that pick is a foreign borrow, not an answer (a bare
|
|
1137
|
+
"1, 2, 3" never names a register-class row, issue #65 B2). A decided
|
|
1138
|
+
line named its family already, so only the undecided line is judged.
|
|
1139
|
+
"""
|
|
1140
|
+
ops = operands_of_key(record.key)
|
|
1141
|
+
if not ops:
|
|
1142
|
+
return True
|
|
1143
|
+
arch, classes, _reverse, _weak = _classify_line(operands, False)
|
|
1144
|
+
if arch:
|
|
1145
|
+
# A decided line named its family; the family-pinned pick stands.
|
|
1146
|
+
return True
|
|
1147
|
+
if "%" in operands or "$" in operands:
|
|
1148
|
+
# AT&T lists sources first and key operands are in Intel order, so
|
|
1149
|
+
# the classes flip to meet the key (the rule pick_record's slots
|
|
1150
|
+
# use). A sigil-less line keeps its order: Intel and ARM both name
|
|
1151
|
+
# the destination first, as the key does.
|
|
1152
|
+
classes = list(reversed(classes))
|
|
1153
|
+
immediates = [cls for cls in classes if cls in ("i", "lit0", "lit1")]
|
|
1154
|
+
if not immediates:
|
|
1155
|
+
return True
|
|
1156
|
+
if len(ops) != len(classes):
|
|
1157
|
+
return False
|
|
1158
|
+
for cls, op in zip(classes, ops):
|
|
1159
|
+
accept = form_accept(op)
|
|
1160
|
+
if (
|
|
1161
|
+
cls in ("i", "lit0", "lit1")
|
|
1162
|
+
and accept
|
|
1163
|
+
and accept not in LITERAL_ACCEPT
|
|
1164
|
+
and accept.isdisjoint(IMMEDIATE_ACCEPT)
|
|
1165
|
+
):
|
|
1166
|
+
return False
|
|
1167
|
+
return True
|
|
1168
|
+
|
|
1169
|
+
|
|
985
1170
|
# ---------------------------------------------------------------------------
|
|
986
1171
|
# Aggregation
|
|
987
1172
|
# ---------------------------------------------------------------------------
|
|
@@ -1219,8 +1404,45 @@ def collect_ports(
|
|
|
1219
1404
|
return ports or None
|
|
1220
1405
|
|
|
1221
1406
|
|
|
1222
|
-
|
|
1223
|
-
|
|
1407
|
+
def _summary_bits(summary: str) -> str | None:
|
|
1408
|
+
"""The digits of the first "NNN - bit" phrase ("32-bit" -> "32"), else None.
|
|
1409
|
+
|
|
1410
|
+
shortcut: str.lower folds no Turkish dotted i in "bit", upgrade if an
|
|
1411
|
+
SDM summary ever carries one
|
|
1412
|
+
"""
|
|
1413
|
+
for j in range(len(summary) - 2):
|
|
1414
|
+
if summary[j : j + 3].lower() != "bit":
|
|
1415
|
+
continue
|
|
1416
|
+
k = j
|
|
1417
|
+
while k > 0 and summary[k - 1].isspace():
|
|
1418
|
+
k -= 1
|
|
1419
|
+
if k > 0 and summary[k - 1] == "-":
|
|
1420
|
+
k -= 1
|
|
1421
|
+
while k > 0 and summary[k - 1].isspace():
|
|
1422
|
+
k -= 1
|
|
1423
|
+
end = k
|
|
1424
|
+
while k > 0 and summary[k - 1].isdecimal():
|
|
1425
|
+
k -= 1
|
|
1426
|
+
if k < end:
|
|
1427
|
+
return summary[k:end]
|
|
1428
|
+
return None
|
|
1429
|
+
|
|
1430
|
+
|
|
1431
|
+
def _form_widths(key: str) -> list[str]:
|
|
1432
|
+
"""The digit groups of the ``M64``-style width tokens of one key."""
|
|
1433
|
+
out: list[str] = []
|
|
1434
|
+
i = 0
|
|
1435
|
+
n = len(key)
|
|
1436
|
+
while i < n:
|
|
1437
|
+
if key[i] in "MRI" and i + 1 < n and key[i + 1].isdecimal():
|
|
1438
|
+
j = i + 1
|
|
1439
|
+
while j < n and key[j].isdecimal():
|
|
1440
|
+
j += 1
|
|
1441
|
+
out.append(key[i + 1 : j])
|
|
1442
|
+
i = j
|
|
1443
|
+
else:
|
|
1444
|
+
i += 1
|
|
1445
|
+
return out
|
|
1224
1446
|
|
|
1225
1447
|
|
|
1226
1448
|
def _summary_matches_form(summary: str, record: InstructionRecord) -> bool:
|
|
@@ -1229,10 +1451,9 @@ def _summary_matches_form(summary: str, record: InstructionRecord) -> bool:
|
|
|
1229
1451
|
|
|
1230
1452
|
We only flag a summary as wrong when it declares an explicit bit-width
|
|
1231
1453
|
that appears nowhere among the form's operand widths."""
|
|
1232
|
-
|
|
1233
|
-
if
|
|
1454
|
+
declared = _summary_bits(summary or "")
|
|
1455
|
+
if declared is None:
|
|
1234
1456
|
return True
|
|
1235
|
-
declared = m.group(1)
|
|
1236
1457
|
widths: list[str] = []
|
|
1237
1458
|
for op in record.operand_details or []:
|
|
1238
1459
|
w = op.get("width")
|
|
@@ -1240,7 +1461,7 @@ def _summary_matches_form(summary: str, record: InstructionRecord) -> bool:
|
|
|
1240
1461
|
widths.append(str(w))
|
|
1241
1462
|
if not widths:
|
|
1242
1463
|
key = str(getattr(record, "key", "") or "")
|
|
1243
|
-
widths =
|
|
1464
|
+
widths = _form_widths(key)
|
|
1244
1465
|
if not widths:
|
|
1245
1466
|
return True
|
|
1246
1467
|
return declared in widths
|
|
@@ -1322,12 +1543,15 @@ def _annotate_instruction(
|
|
|
1322
1543
|
line_isa: str | None = None,
|
|
1323
1544
|
line_number: int | None = None,
|
|
1324
1545
|
identity_cache: dict[str, tuple[InstructionRecord, PerfSummary | None]] | None = None,
|
|
1546
|
+
foreign: bool = False,
|
|
1325
1547
|
) -> tuple[str, dict[str, Any] | None]:
|
|
1326
1548
|
"""Return the rendered output line and an optional JSON record.
|
|
1327
1549
|
|
|
1328
1550
|
``line_isa`` names the family the line's own evidence shows (a pinned
|
|
1329
1551
|
``--isa`` counts as one). None marks a neutral line in auto mode: the
|
|
1330
|
-
stream family answers first, then the family that holds rows.
|
|
1552
|
+
stream family answers first, then the family that holds rows. ``foreign``
|
|
1553
|
+
marks a line whose operands parse as no supported arch's operands
|
|
1554
|
+
(PPC, MIPS, ARM32): no supported family's rows answer it (issue #65 B2/B6).
|
|
1331
1555
|
"""
|
|
1332
1556
|
if stats is not None:
|
|
1333
1557
|
stats["parsed"] = stats.get("parsed", 0) + 1
|
|
@@ -1392,7 +1616,9 @@ def _annotate_instruction(
|
|
|
1392
1616
|
else:
|
|
1393
1617
|
isa = line_isa or opts.isa
|
|
1394
1618
|
pick_isa = isa
|
|
1395
|
-
if
|
|
1619
|
+
if foreign:
|
|
1620
|
+
records = []
|
|
1621
|
+
elif lookup_cache is not None:
|
|
1396
1622
|
records = lookup_cache.get((parsed.mnemonic, isa))
|
|
1397
1623
|
if records is None:
|
|
1398
1624
|
# Module-level call (not a captured reference) so tests can
|
|
@@ -1401,16 +1627,21 @@ def _annotate_instruction(
|
|
|
1401
1627
|
lookup_cache[(parsed.mnemonic, isa)] = records
|
|
1402
1628
|
else:
|
|
1403
1629
|
records = lookup(parsed.mnemonic, conn, isa=isa)
|
|
1404
|
-
if not records and line_isa is None:
|
|
1630
|
+
if not records and line_isa is None and not foreign:
|
|
1405
1631
|
# A neutral line whose stream family holds no rows gets the rows
|
|
1406
1632
|
# of the family that has them (issue #57 batch: jnle, bare b).
|
|
1633
|
+
# A foreign line borrows nothing (issue #65 B2/B6).
|
|
1407
1634
|
pick_isa = None
|
|
1408
1635
|
records = lookup(parsed.mnemonic, conn, isa=None)
|
|
1409
1636
|
if lookup_cache is not None:
|
|
1410
1637
|
lookup_cache[(parsed.mnemonic, None)] = records
|
|
1411
|
-
if stats is not None and records:
|
|
1412
|
-
stats["recognized"] = stats.get("recognized", 0) + 1
|
|
1413
1638
|
record = pick_record(records, arch=opts.arch, operands=parsed.operands, isa=pick_isa)
|
|
1639
|
+
if record is not None and not _record_takes_line(record, parsed.operands):
|
|
1640
|
+
# The key does not take the line's operand classes: a foreign
|
|
1641
|
+
# borrow, not an answer (issue #65 B2).
|
|
1642
|
+
record = None
|
|
1643
|
+
if stats is not None and record is not None:
|
|
1644
|
+
stats["recognized"] = stats.get("recognized", 0) + 1
|
|
1414
1645
|
|
|
1415
1646
|
if record is None:
|
|
1416
1647
|
if opts.unknown == "drop":
|
|
@@ -1569,6 +1800,7 @@ def annotate_stream(
|
|
|
1569
1800
|
}
|
|
1570
1801
|
auto_isa = opts.isa not in ("x86", "arm", "riscv")
|
|
1571
1802
|
line_family: dict[int, str] = {}
|
|
1803
|
+
foreign: set[int] = set()
|
|
1572
1804
|
if auto_isa:
|
|
1573
1805
|
# Assembler acceptance decides each line's family: one llvm-mc run
|
|
1574
1806
|
# per candidate triple. The register vote below is the fallback when
|
|
@@ -1576,6 +1808,13 @@ def annotate_stream(
|
|
|
1576
1808
|
probe_texts = [text for _index, text in items]
|
|
1577
1809
|
families, majority = isa_by_acceptance(probe_texts)
|
|
1578
1810
|
line_family = {index: family for (index, _text), family in zip(items, families) if family}
|
|
1811
|
+
# The line's own parse names foreign architectures (PPC/MIPS/ARM32):
|
|
1812
|
+
# those lines borrow no rows from a supported family (issue #65 B2/B6).
|
|
1813
|
+
foreign = {
|
|
1814
|
+
index
|
|
1815
|
+
for index, parsed in enumerate(parsed_lines)
|
|
1816
|
+
if parsed.kind == LineKind.INSTRUCTION and _foreign_operands(parsed.operands)
|
|
1817
|
+
}
|
|
1579
1818
|
# auto: the acceptance majority names the ISA, then the input
|
|
1580
1819
|
# signals, then the document arch from _document_arch; x86 stays
|
|
1581
1820
|
# the guess when all stay silent.
|
|
@@ -1612,6 +1851,11 @@ def annotate_stream(
|
|
|
1612
1851
|
if not collecting_json:
|
|
1613
1852
|
yield parsed.raw + "\n"
|
|
1614
1853
|
continue
|
|
1854
|
+
line_isa = opts.isa
|
|
1855
|
+
if auto_isa:
|
|
1856
|
+
# A pin forces one family; auto mode splits the batch per line,
|
|
1857
|
+
# the assembler verdict first and the register vote as fallback.
|
|
1858
|
+
line_isa = line_family.get(index) or _line_isa(f"{parsed.mnemonic} {parsed.operands}")
|
|
1615
1859
|
out_line, record = _annotate_instruction(
|
|
1616
1860
|
parsed,
|
|
1617
1861
|
opts,
|
|
@@ -1619,13 +1863,10 @@ def annotate_stream(
|
|
|
1619
1863
|
lookup_cache,
|
|
1620
1864
|
stats,
|
|
1621
1865
|
identity_key=identity.get(index),
|
|
1622
|
-
|
|
1623
|
-
# the assembler verdict first and the register vote as fallback.
|
|
1624
|
-
line_isa=opts.isa
|
|
1625
|
-
if not auto_isa
|
|
1626
|
-
else line_family.get(index) or _line_isa(f"{parsed.mnemonic} {parsed.operands}"),
|
|
1866
|
+
line_isa=line_isa,
|
|
1627
1867
|
line_number=index + 1,
|
|
1628
1868
|
identity_cache=identity_cache,
|
|
1869
|
+
foreign=index in foreign if line_isa is None else False,
|
|
1629
1870
|
)
|
|
1630
1871
|
if collecting_json:
|
|
1631
1872
|
if record is not None:
|