simdref 0.0.17__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.17/src/simdref.egg-info → simdref-0.0.18}/PKG-INFO +3 -2
- {simdref-0.0.17 → simdref-0.0.18}/README.md +2 -1
- {simdref-0.0.17 → simdref-0.0.18}/pyproject.toml +1 -1
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/annotate.py +138 -14
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/asm_identity.py +35 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/catkey.py +36 -2
- simdref-0.0.18/src/simdref/cuda.py +50 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/display.py +13 -2
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/filters.py +1 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/ingest_catalog.py +7 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/ingest_sources.py +22 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/lsp.py +27 -8
- {simdref-0.0.17 → simdref-0.0.18/src/simdref.egg-info}/PKG-INFO +3 -2
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref.egg-info/SOURCES.txt +10 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_asm_wire.py +73 -4
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_catkey.py +2 -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_issue_65_branch_target.py +91 -0
- simdref-0.0.18/tests/test_issue_65_foreign_arch.py +236 -0
- 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.17 → 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.17 → simdref-0.0.18}/tests/test_lsp_stdio.py +22 -15
- {simdref-0.0.17 → simdref-0.0.18}/LICENSE +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/setup.cfg +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/__init__.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/__main__.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/arm_instructions.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/asmline.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/cli.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/export.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/htmltext.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/ingest.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/ingest_pdf.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/manpages.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/models.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/operands.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/pdfparse/__init__.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/pdfparse/base.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/pdfparse/intel.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/pdfparse/registry.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/pdfparse/types.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/pdfrefs.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/__init__.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/amd_swopt.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/arm_swopt.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/cores.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/instlatx64.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/intel_guide.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/intel_optref.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/llvm_mca.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/llvm_scheduling.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/perf_sources/merge.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/ptx.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/queries.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/regtable.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/riscv.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/search.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/storage.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/tablecols.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/tui.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref/ui_labels.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref.egg-info/dependency_links.txt +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref.egg-info/entry_points.txt +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref.egg-info/requires.txt +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/src/simdref.egg-info/top_level.txt +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_a1_sdm_parser.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_a2_perf_rule.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_a4_arm_pdfrefs.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_a5_offline_aarchmrs.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_amd_match_llvm_mc.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_annotate.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_annotate_positions.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_annotate_scans.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_arrangement_tail.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_asm_identity.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_asmline.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_audit_coverage.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_auto_update_on_version_change.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b1_latency_pairs.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b2_metadata.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b3_sdm_forms.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b4_descriptions_provenance.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b5_linking.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b5_perf_family.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_b5_sme.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_c0_perf_priority.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_cli_bare_query.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_cli_bootstrap_progress.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_cli_help.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_cli_llm.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_coverage_live.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_coverage_parity.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_display.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_display_scanners.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_export.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_export_viz.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_fetch_retry.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_filters.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_fixui_display.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_htmltext.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_ingest_enc_fields.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_ingest_sources.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_int_arm_isa_label.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_int_core_ids.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_int_ingest_registration.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_int_sdm_family_fallback.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_intel_operation_and_url.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_isa_detect_llvm_mc.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue2_fixes.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_51.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_52.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_53_54.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_55.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_56_57a.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_57_cpi.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_60_c1.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_60_contract.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_60_crossarch.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_60_matcher.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_arm_alias.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_compressed.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_identity.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_m1.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_perfdata.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_text.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issue_65_timing.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issues_23_24_25.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_issues_51_57_join.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_arm_postindex.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_att_movx.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_autoupdate.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_catalog_gaps.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_ingest_isa_merge.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_intel_size.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_perf_join.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_leftover_riscv_shared.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_lsp_ptx.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_lsp_web.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_models.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_no_catalog_row.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_objdump_listings.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_operands.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_pdfparse.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf_source_amd_swopt.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf_source_arm_swopt.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf_source_instlatx64.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf_source_intel_guide.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf_source_intel_optref.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_perf_sources.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_presentation.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_preset_persistence.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_ptx.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_regtable.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_release_tag_pin.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_riscv_docs_pages.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_search.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_search_pushdown.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_shared_source_helpers.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_source_kind_filter.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_source_validation.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_storage_alnum.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_storage_payload.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_storage_schema.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_tablecols.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_tui.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_ui_labels_parity.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_update_build_stamp.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_vendor_riscv_categories.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_vendor_riscv_sources.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_version_matches_pyproject.py +0 -0
- {simdref-0.0.17 → simdref-0.0.18}/tests/test_x86_key_widths.py +0 -0
- {simdref-0.0.17 → 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
|
|
@@ -177,7 +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 |
|
|
180
|
+
| NVIDIA PTX ISA | Instruction ops, syntax, qualifiers, versions, sm_XX | 478 instructions |
|
|
181
|
+
| CUDA intrinsics | Device intrinsics from clang CUDA headers | 585 intrinsics |
|
|
181
182
|
| Intel Guide perf tables | Per-core latency and throughput | 9,134 rows |
|
|
182
183
|
| Intel Optimization Ref. | Latency and throughput tables (volume 2) | 3,155 rows |
|
|
183
184
|
| InstLatX64 | Measured latency and throughput dumps | 16,235 rows |
|
|
@@ -142,7 +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 |
|
|
145
|
+
| NVIDIA PTX ISA | Instruction ops, syntax, qualifiers, versions, sm_XX | 478 instructions |
|
|
146
|
+
| CUDA intrinsics | Device intrinsics from clang CUDA headers | 585 intrinsics |
|
|
146
147
|
| Intel Guide perf tables | Per-core latency and throughput | 9,134 rows |
|
|
147
148
|
| Intel Optimization Ref. | Latency and throughput tables (volume 2) | 3,155 rows |
|
|
148
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"
|
|
@@ -19,8 +19,17 @@ from typing import Any, Iterable, Iterator
|
|
|
19
19
|
from simdref import perf
|
|
20
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(
|
|
@@ -616,6 +626,42 @@ def _voter_names():
|
|
|
616
626
|
|
|
617
627
|
_EXCL_ARM, _SHARED, _RISCV = _voter_names()
|
|
618
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
|
+
|
|
619
665
|
|
|
620
666
|
def _isa_votes(text: str) -> tuple[int, int, int]:
|
|
621
667
|
"""(excl_arm, excl_riscv, shared) register votes of one instruction text."""
|
|
@@ -630,7 +676,9 @@ def _isa_votes(text: str) -> tuple[int, int, int]:
|
|
|
630
676
|
while j < n and (text[j] == "_" or text[j].isalnum()):
|
|
631
677
|
j += 1
|
|
632
678
|
token = text[i:j]
|
|
633
|
-
|
|
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 "%$":
|
|
634
682
|
if token in _EXCL_ARM:
|
|
635
683
|
excl_arm += 1
|
|
636
684
|
elif token in _SHARED:
|
|
@@ -1047,6 +1095,19 @@ def pick_record(
|
|
|
1047
1095
|
# AT&T lists sources first; catalog form tokens are in Intel order.
|
|
1048
1096
|
slots = list(reversed(slots))
|
|
1049
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
|
+
|
|
1050
1111
|
def score(rec: InstructionRecord) -> tuple[int, int, int, int]:
|
|
1051
1112
|
measured = sum(
|
|
1052
1113
|
1
|
|
@@ -1063,6 +1124,49 @@ def pick_record(
|
|
|
1063
1124
|
return max(candidates, key=score)
|
|
1064
1125
|
|
|
1065
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
|
+
|
|
1066
1170
|
# ---------------------------------------------------------------------------
|
|
1067
1171
|
# Aggregation
|
|
1068
1172
|
# ---------------------------------------------------------------------------
|
|
@@ -1439,12 +1543,15 @@ def _annotate_instruction(
|
|
|
1439
1543
|
line_isa: str | None = None,
|
|
1440
1544
|
line_number: int | None = None,
|
|
1441
1545
|
identity_cache: dict[str, tuple[InstructionRecord, PerfSummary | None]] | None = None,
|
|
1546
|
+
foreign: bool = False,
|
|
1442
1547
|
) -> tuple[str, dict[str, Any] | None]:
|
|
1443
1548
|
"""Return the rendered output line and an optional JSON record.
|
|
1444
1549
|
|
|
1445
1550
|
``line_isa`` names the family the line's own evidence shows (a pinned
|
|
1446
1551
|
``--isa`` counts as one). None marks a neutral line in auto mode: the
|
|
1447
|
-
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).
|
|
1448
1555
|
"""
|
|
1449
1556
|
if stats is not None:
|
|
1450
1557
|
stats["parsed"] = stats.get("parsed", 0) + 1
|
|
@@ -1509,7 +1616,9 @@ def _annotate_instruction(
|
|
|
1509
1616
|
else:
|
|
1510
1617
|
isa = line_isa or opts.isa
|
|
1511
1618
|
pick_isa = isa
|
|
1512
|
-
if
|
|
1619
|
+
if foreign:
|
|
1620
|
+
records = []
|
|
1621
|
+
elif lookup_cache is not None:
|
|
1513
1622
|
records = lookup_cache.get((parsed.mnemonic, isa))
|
|
1514
1623
|
if records is None:
|
|
1515
1624
|
# Module-level call (not a captured reference) so tests can
|
|
@@ -1518,16 +1627,21 @@ def _annotate_instruction(
|
|
|
1518
1627
|
lookup_cache[(parsed.mnemonic, isa)] = records
|
|
1519
1628
|
else:
|
|
1520
1629
|
records = lookup(parsed.mnemonic, conn, isa=isa)
|
|
1521
|
-
if not records and line_isa is None:
|
|
1630
|
+
if not records and line_isa is None and not foreign:
|
|
1522
1631
|
# A neutral line whose stream family holds no rows gets the rows
|
|
1523
1632
|
# of the family that has them (issue #57 batch: jnle, bare b).
|
|
1633
|
+
# A foreign line borrows nothing (issue #65 B2/B6).
|
|
1524
1634
|
pick_isa = None
|
|
1525
1635
|
records = lookup(parsed.mnemonic, conn, isa=None)
|
|
1526
1636
|
if lookup_cache is not None:
|
|
1527
1637
|
lookup_cache[(parsed.mnemonic, None)] = records
|
|
1528
|
-
if stats is not None and records:
|
|
1529
|
-
stats["recognized"] = stats.get("recognized", 0) + 1
|
|
1530
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
|
|
1531
1645
|
|
|
1532
1646
|
if record is None:
|
|
1533
1647
|
if opts.unknown == "drop":
|
|
@@ -1686,6 +1800,7 @@ def annotate_stream(
|
|
|
1686
1800
|
}
|
|
1687
1801
|
auto_isa = opts.isa not in ("x86", "arm", "riscv")
|
|
1688
1802
|
line_family: dict[int, str] = {}
|
|
1803
|
+
foreign: set[int] = set()
|
|
1689
1804
|
if auto_isa:
|
|
1690
1805
|
# Assembler acceptance decides each line's family: one llvm-mc run
|
|
1691
1806
|
# per candidate triple. The register vote below is the fallback when
|
|
@@ -1693,6 +1808,13 @@ def annotate_stream(
|
|
|
1693
1808
|
probe_texts = [text for _index, text in items]
|
|
1694
1809
|
families, majority = isa_by_acceptance(probe_texts)
|
|
1695
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
|
+
}
|
|
1696
1818
|
# auto: the acceptance majority names the ISA, then the input
|
|
1697
1819
|
# signals, then the document arch from _document_arch; x86 stays
|
|
1698
1820
|
# the guess when all stay silent.
|
|
@@ -1729,6 +1851,11 @@ def annotate_stream(
|
|
|
1729
1851
|
if not collecting_json:
|
|
1730
1852
|
yield parsed.raw + "\n"
|
|
1731
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}")
|
|
1732
1859
|
out_line, record = _annotate_instruction(
|
|
1733
1860
|
parsed,
|
|
1734
1861
|
opts,
|
|
@@ -1736,13 +1863,10 @@ def annotate_stream(
|
|
|
1736
1863
|
lookup_cache,
|
|
1737
1864
|
stats,
|
|
1738
1865
|
identity_key=identity.get(index),
|
|
1739
|
-
|
|
1740
|
-
# the assembler verdict first and the register vote as fallback.
|
|
1741
|
-
line_isa=opts.isa
|
|
1742
|
-
if not auto_isa
|
|
1743
|
-
else line_family.get(index) or _line_isa(f"{parsed.mnemonic} {parsed.operands}"),
|
|
1866
|
+
line_isa=line_isa,
|
|
1744
1867
|
line_number=index + 1,
|
|
1745
1868
|
identity_cache=identity_cache,
|
|
1869
|
+
foreign=index in foreign if line_isa is None else False,
|
|
1746
1870
|
)
|
|
1747
1871
|
if collecting_json:
|
|
1748
1872
|
if record is not None:
|
|
@@ -1403,6 +1403,40 @@ _ISA_CANDIDATES = (
|
|
|
1403
1403
|
)
|
|
1404
1404
|
|
|
1405
1405
|
|
|
1406
|
+
def _reloc_branch_targets(text: str) -> str:
|
|
1407
|
+
"""Spell objdump's bare branch targets the fixup way ("je 7f37b" as ".+0x7f37b").
|
|
1408
|
+
|
|
1409
|
+
objdump prints branch targets as bare hex, and llvm-mc rejects the bare
|
|
1410
|
+
spelling where the value holds an a-f letter: "7f37b" is no decimal
|
|
1411
|
+
number and no valid symbol. The ".+0x" spelling is the same PC-relative
|
|
1412
|
+
fixup the <sym> form answers with, and it carries no immediate range
|
|
1413
|
+
check, so it assembles on every candidate triple for any address. A
|
|
1414
|
+
token qualifies only when it starts with a digit, holds only hex digits
|
|
1415
|
+
with at least one a-f letter (a decimal immediate assembles as-is), and
|
|
1416
|
+
follows a separator, so the tail of "0x...", "0b..." or "#0x..." text is
|
|
1417
|
+
never rewritten.
|
|
1418
|
+
"""
|
|
1419
|
+
hexd = "0123456789abcdefABCDEF"
|
|
1420
|
+
letters = "abcdefABCDEF"
|
|
1421
|
+
out: list[str] = []
|
|
1422
|
+
i = 0
|
|
1423
|
+
n = len(text)
|
|
1424
|
+
while i < n:
|
|
1425
|
+
if text[i].isdigit() and (i == 0 or text[i - 1] in " \t,"):
|
|
1426
|
+
j = i
|
|
1427
|
+
while j < n and text[j] in hexd:
|
|
1428
|
+
j += 1
|
|
1429
|
+
token = text[i:j]
|
|
1430
|
+
if any(c in letters for c in token):
|
|
1431
|
+
out.append(".+0x")
|
|
1432
|
+
out.append(token)
|
|
1433
|
+
i = j
|
|
1434
|
+
continue
|
|
1435
|
+
out.append(text[i])
|
|
1436
|
+
i += 1
|
|
1437
|
+
return "".join(out)
|
|
1438
|
+
|
|
1439
|
+
|
|
1406
1440
|
def _probe_all_triples(lines: list[str], llvm_mc: str) -> dict[tuple[str, str], list[dict]]:
|
|
1407
1441
|
"""Probe every candidate triple, one llvm-mc run per triple.
|
|
1408
1442
|
|
|
@@ -1412,6 +1446,7 @@ def _probe_all_triples(lines: list[str], llvm_mc: str) -> dict[tuple[str, str],
|
|
|
1412
1446
|
"""
|
|
1413
1447
|
from concurrent.futures import ThreadPoolExecutor
|
|
1414
1448
|
|
|
1449
|
+
lines = [_reloc_branch_targets(line) for line in lines]
|
|
1415
1450
|
with ThreadPoolExecutor(max_workers=len(_ISA_CANDIDATES)) as pool:
|
|
1416
1451
|
futures = {
|
|
1417
1452
|
(arch, syntax): pool.submit(_probe_lines_split, lines, arch, syntax, llvm_mc)
|
|
@@ -68,6 +68,7 @@ ISA_FAMILY = {
|
|
|
68
68
|
"riscv32": "riscv",
|
|
69
69
|
"riscv64": "riscv",
|
|
70
70
|
"ptx": "ptx",
|
|
71
|
+
"cuda": "cuda",
|
|
71
72
|
}
|
|
72
73
|
|
|
73
74
|
|
|
@@ -84,6 +85,8 @@ def isa_family(architecture: str | None) -> str | None:
|
|
|
84
85
|
return "riscv"
|
|
85
86
|
if name.startswith("ptx"):
|
|
86
87
|
return "ptx"
|
|
88
|
+
if name.startswith("cuda"):
|
|
89
|
+
return "cuda"
|
|
87
90
|
return "x86"
|
|
88
91
|
|
|
89
92
|
|
|
@@ -150,6 +153,35 @@ def _first_digits(text: str) -> int | None:
|
|
|
150
153
|
return int(text[start:end]) if end > start else None
|
|
151
154
|
|
|
152
155
|
|
|
156
|
+
def names_za_tile(text: str) -> bool:
|
|
157
|
+
"""True when *text* names a ZA tile operand ("za0h", "za.s"), never ZT0.
|
|
158
|
+
|
|
159
|
+
A ZA tile name starts with "za" at an operand boundary and closes the
|
|
160
|
+
stem with a tile digit ("za0h"), a tile element letter ("zab") or the
|
|
161
|
+
full-register dot ("za.s"). One boundary scan; no regular expressions.
|
|
162
|
+
"""
|
|
163
|
+
low = text.casefold()
|
|
164
|
+
for at in range(len(low) - 2):
|
|
165
|
+
if low[at] != "z" or low[at + 1] != "a":
|
|
166
|
+
continue
|
|
167
|
+
if at > 0 and (low[at - 1].isalnum() or low[at - 1] in "._"):
|
|
168
|
+
continue
|
|
169
|
+
if low[at + 2].isdecimal() or low[at + 2] in ".bhwdq":
|
|
170
|
+
return True
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def key_names_za_tile(key: str) -> bool:
|
|
175
|
+
"""True when a catalog key names a ZA tile operand form.
|
|
176
|
+
|
|
177
|
+
The tile encodes as a placeholder ("<ZAT...>"), the tile-slice register
|
|
178
|
+
("<WV>") or a literal name ("ZA0<HV>"). ZT0 rows stay out: SVE counted
|
|
179
|
+
register-list keys reuse "<ZT..>" placeholder names for plain Z lists.
|
|
180
|
+
"""
|
|
181
|
+
upper = key.upper()
|
|
182
|
+
return "<ZA" in upper or "<WV>" in upper or names_za_tile(key)
|
|
183
|
+
|
|
184
|
+
|
|
153
185
|
def form_accept(op: str) -> frozenset:
|
|
154
186
|
"""The line classes a catalog-key operand accepts, as one frozenset.
|
|
155
187
|
|
|
@@ -200,8 +232,10 @@ def form_accept(op: str) -> frozenset:
|
|
|
200
232
|
name = _cut_key_repeats(op.upper())
|
|
201
233
|
if "<HASH>" in name:
|
|
202
234
|
return IMMEDIATE_ACCEPT
|
|
203
|
-
# A ZA tile operand ("<ZAT...>[W1, #0]"
|
|
204
|
-
|
|
235
|
+
# A ZA tile operand ("<ZAT...>[W1, #0]", "ZA0<HV>.B[W1, #0]",
|
|
236
|
+
# "Z3.4S[<WV>, #0]") is not a plain vector register: it matches a line's
|
|
237
|
+
# ZA tile operand only.
|
|
238
|
+
if key_names_za_tile(op):
|
|
205
239
|
return frozenset({"za"})
|
|
206
240
|
if _is_counted_vector(name):
|
|
207
241
|
# A counted register list accepts only the same count.
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""CUDA C++ device intrinsics parsing and derived record rows.
|
|
2
|
+
|
|
3
|
+
Parses the JSON bundle produced by tools/vendor_cuda_sources.py, which
|
|
4
|
+
extracts CUDA API names from four clang headers and resolves PTX
|
|
5
|
+
instruction links by parsing LLVM TableGen .td files.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import json
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from simdref.models import IntrinsicRecord
|
|
14
|
+
|
|
15
|
+
BUNDLE_FORMAT = "clang-cuda-headers-v2"
|
|
16
|
+
CUDA_INTRINSICS_DOC_URL = "https://docs.nvidia.com/cuda/cuda-c-programming-guide/"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def parse_cuda_intrinsics_payload(text: str) -> list[IntrinsicRecord]:
|
|
20
|
+
"""Parse the CUDA intrinsics JSON bundle into IntrinsicRecord rows."""
|
|
21
|
+
data = json.loads(text)
|
|
22
|
+
if data.get("format") != BUNDLE_FORMAT:
|
|
23
|
+
raise ValueError(f"Unexpected bundle format: {data.get('format')!r}")
|
|
24
|
+
|
|
25
|
+
rows: list[IntrinsicRecord] = []
|
|
26
|
+
for item in data.get("intrinsics", []):
|
|
27
|
+
metadata = item.get("metadata", {})
|
|
28
|
+
classification = metadata.get("classification", "")
|
|
29
|
+
|
|
30
|
+
row = IntrinsicRecord(
|
|
31
|
+
name=item["name"],
|
|
32
|
+
signature=item.get("signature", ""),
|
|
33
|
+
description=item.get("description", ""),
|
|
34
|
+
header=item.get("header", ""),
|
|
35
|
+
url=item.get("url", ""),
|
|
36
|
+
architecture="cuda",
|
|
37
|
+
isa=["CUDA"],
|
|
38
|
+
category=item.get("category", ""),
|
|
39
|
+
subcategory=item.get("subcategory", ""),
|
|
40
|
+
instructions=item.get("instructions", []),
|
|
41
|
+
instruction_refs=item.get("instruction_refs", []),
|
|
42
|
+
metadata={"classification": classification},
|
|
43
|
+
doc_sections=item.get("doc_sections", {}),
|
|
44
|
+
notes=item.get("notes", []),
|
|
45
|
+
aliases=item.get("aliases", []),
|
|
46
|
+
source="clang-cuda-headers",
|
|
47
|
+
)
|
|
48
|
+
rows.append(row)
|
|
49
|
+
|
|
50
|
+
return rows
|
|
@@ -367,6 +367,7 @@ def display_architecture(architecture: str) -> str:
|
|
|
367
367
|
"x86": "x86",
|
|
368
368
|
"arm": "Arm",
|
|
369
369
|
"riscv": "RISC-V",
|
|
370
|
+
"cuda": "CUDA",
|
|
370
371
|
}.get((architecture or "").lower(), architecture or "-")
|
|
371
372
|
|
|
372
373
|
|
|
@@ -390,6 +391,8 @@ def isa_family(isa: str) -> str:
|
|
|
390
391
|
return "RISC-V"
|
|
391
392
|
if d == "PTX":
|
|
392
393
|
return "PTX"
|
|
394
|
+
if d == "CUDA":
|
|
395
|
+
return "CUDA"
|
|
393
396
|
if d.startswith("APX"):
|
|
394
397
|
return "APX"
|
|
395
398
|
if d.startswith("AMX"):
|
|
@@ -507,10 +510,17 @@ def instruction_markdown(item, conn=None) -> str:
|
|
|
507
510
|
"""
|
|
508
511
|
lines = [f"```asm\n{display_instruction_form(item.key)}\n```"]
|
|
509
512
|
lines.append(f"**isa.** {display_isa(item.isa)}")
|
|
510
|
-
|
|
513
|
+
# The web app keys an entry by db_key. A display-key link opens no
|
|
514
|
+
# detail page. It fills the query box instead.
|
|
515
|
+
link = f"[simdref]({web_deep_link('instruction', item.db_key)})"
|
|
516
|
+
rows = instruction_metadata_rows(item)
|
|
517
|
+
for key, value in rows:
|
|
511
518
|
if key != "isa":
|
|
512
519
|
# Escaping a URL would break the editor's autolink.
|
|
513
|
-
|
|
520
|
+
row = f"**{key}.** {value if value.startswith('http') else _md_escape(value)}"
|
|
521
|
+
lines.append(f"{row} | {link}" if key == "url" else row)
|
|
522
|
+
if not any(key == "url" for key, _ in rows):
|
|
523
|
+
lines.append(link)
|
|
514
524
|
if item.description:
|
|
515
525
|
shown: set[str] = set()
|
|
516
526
|
for section in _DESCRIPTION_ORDER:
|
|
@@ -1196,6 +1206,7 @@ def render_intrinsic(catalog, item, conn=None, short: bool = False, full: bool =
|
|
|
1196
1206
|
table.add_row("source", item.url)
|
|
1197
1207
|
table.add_row("architecture", display_architecture(item.architecture))
|
|
1198
1208
|
table.add_row("isa", ", ".join(item.isa) or "-")
|
|
1209
|
+
table.add_row("instructions", ", ".join(item.instructions) or "-")
|
|
1199
1210
|
table.add_row("category", category_display_text(item))
|
|
1200
1211
|
if item.description:
|
|
1201
1212
|
table.add_row("description", item.description)
|
|
@@ -21,12 +21,14 @@ from simdref.catkey import (
|
|
|
21
21
|
parse_key,
|
|
22
22
|
substitute_placeholders,
|
|
23
23
|
)
|
|
24
|
+
from simdref.cuda import parse_cuda_intrinsics_payload
|
|
24
25
|
from simdref.display import strip_instruction_decorators, strip_leading_tags
|
|
25
26
|
from simdref.htmltext import squeeze, strip_html, tidy, unwrap_anchors
|
|
26
27
|
from simdref.ingest_pdf import find_pdf_source_path, load_or_parse_pdf_source, merge_pdf_enrichment
|
|
27
28
|
from simdref.ingest_sources import (
|
|
28
29
|
fetch_arm_a64_data,
|
|
29
30
|
fetch_arm_acle_data,
|
|
31
|
+
fetch_cuda_data,
|
|
30
32
|
fetch_intel_data,
|
|
31
33
|
fetch_ptx_isa_data,
|
|
32
34
|
fetch_riscv_rvv_intrinsics_data,
|
|
@@ -1622,11 +1624,15 @@ def build_catalog(
|
|
|
1622
1624
|
emit("Fetching PTX ISA reference")
|
|
1623
1625
|
ptx_text, ptx_source = fetch_ptx_isa_data()
|
|
1624
1626
|
emit(f"Fetched PTX ISA reference from {ptx_source.url}")
|
|
1627
|
+
emit("Fetching CUDA intrinsics data")
|
|
1628
|
+
cuda_text, cuda_source = fetch_cuda_data()
|
|
1629
|
+
emit(f"Fetched CUDA intrinsics data from {cuda_source.url}")
|
|
1625
1630
|
emit("Parsing intrinsic catalog")
|
|
1626
1631
|
intrinsics = parse_intel_payload(intel_text)
|
|
1627
1632
|
arm_intrinsics = parse_arm_intrinsics_payload(arm_acle_text)
|
|
1628
1633
|
intrinsics.extend(arm_intrinsics)
|
|
1629
1634
|
intrinsics.extend(parse_riscv_intrinsics_payload(riscv_intrinsics_text))
|
|
1635
|
+
intrinsics.extend(parse_cuda_intrinsics_payload(cuda_text))
|
|
1630
1636
|
emit(f"Parsed {len(intrinsics)} intrinsics")
|
|
1631
1637
|
emit("Parsing instruction catalog")
|
|
1632
1638
|
instructions = parse_uops_xml(uops_text)
|
|
@@ -1668,6 +1674,7 @@ def build_catalog(
|
|
|
1668
1674
|
riscv_instructions_source,
|
|
1669
1675
|
*perf_sources_version,
|
|
1670
1676
|
ptx_source,
|
|
1677
|
+
cuda_source,
|
|
1671
1678
|
],
|
|
1672
1679
|
generated_at=now_iso(),
|
|
1673
1680
|
)
|
|
@@ -132,6 +132,9 @@ LOCAL_PTX_BUNDLES = [
|
|
|
132
132
|
LOCAL_PTX_DOC_HTMLS = [
|
|
133
133
|
_REPO_ROOT / "vendor" / "ptx" / "parallel-thread-execution.html",
|
|
134
134
|
]
|
|
135
|
+
LOCAL_CUDA_BUNDLES = [
|
|
136
|
+
_REPO_ROOT / "vendor" / "cuda" / "cuda_intrinsics.json",
|
|
137
|
+
]
|
|
135
138
|
|
|
136
139
|
|
|
137
140
|
def now_iso() -> str:
|
|
@@ -834,3 +837,22 @@ def fetch_ptx_isa_data() -> tuple[str, SourceVersion]:
|
|
|
834
837
|
fetched_at=now_iso(),
|
|
835
838
|
url=PTX_ISA_DOC_URL,
|
|
836
839
|
)
|
|
840
|
+
|
|
841
|
+
|
|
842
|
+
CUDA_INTRINSICS_DOC_URL = "https://docs.nvidia.com/cuda/cuda-c-programming-guide/"
|
|
843
|
+
|
|
844
|
+
|
|
845
|
+
def fetch_cuda_data() -> tuple[str, SourceVersion]:
|
|
846
|
+
local = _read_local_text(LOCAL_CUDA_BUNDLES, "cuda-intrinsics", "local-bundle")
|
|
847
|
+
if local is not None:
|
|
848
|
+
return local
|
|
849
|
+
# Return empty bundle if not found - CUDA intrinsics are optional
|
|
850
|
+
empty_bundle = (
|
|
851
|
+
'{"format": "clang-cuda-headers-v2", "intrinsics": [], "metadata": {"total_intrinsics": 0}}'
|
|
852
|
+
)
|
|
853
|
+
return empty_bundle, SourceVersion(
|
|
854
|
+
source="cuda-intrinsics",
|
|
855
|
+
version="empty",
|
|
856
|
+
fetched_at=now_iso(),
|
|
857
|
+
url="local:empty",
|
|
858
|
+
)
|