课题12:RISC-V后端指令计数统计器

难度:中 | 类型:项目实战 | 源文件scratchv/backend/inst_counter.py | 行数:~350 状态:✅ 已完成


概述

解析生成的RISC-V汇编,统计不同类型指令的数量(算术、逻辑、访存、分支等),生成分类统计报告。


理解背景

是什么?

指令计数统计器(inst_counter.py)解析 RISC-V 汇编文件,按类别统计指令数量,并输出文本表格和 HTML 报告。

统计类别:

类别 包含的指令 例子
ALU 算术/逻辑运算 add, sub, mul, srai, and, or, addi, lui, slt
MEM 内存读写 lw, sw, lh, sh, flw, fsw
BRANCH 条件分支 beq, bne, blt, bge
JUMP 无条件跳转 j, jal, jalr, ret
PSEUDO 伪指令(汇编语法糖) li, mv, nop, call
MISC 其他/未知 无法归类的指令

为什么?

编译器优化的核心指标之一就是指令数。你需要知道:

  1. 各类指令占比:ALU 多 = 计算密集,MEM 多 = 数据密集
  2. 优化前后对比:优化到底减少了多少条指令?哪一类减少最多?
  3. ScratchV vs LLVM:两个编译器的指令分布有什么不同?

代码走读

命令行使用
# 先编译 CNN 模型到汇编
python3 scratchv/standalone/onnx_to_riscv_standalone.py models/graph/cnn.onnx \
    -o /tmp/cnn_riscv.bin

# 统计汇编文件的指令分布
python3 -m scratchv.backend.inst_counter /tmp/cnn_riscv.s

实际输出(CNN 模型,887 条指令):

============================================================
RISC-V Instruction Statistics
============================================================
Category      Count    Percent Bar
------------------------------------------------------------
ALU             604      68.1% ##################################
MEM              92      10.4% #####
BRANCH           48       5.4% ##
JUMP              3       0.3% #
PSEUDO          140      15.8% #######
MISC              0       0.0% #
------------------------------------------------------------
TOTAL           887

------------------------------------------------------------
Per-instruction Breakdown
------------------------------------------------------------
  addi            171  (ALU)
  add             154  (ALU)
  li              108  (PSEUDO)
  lui              77  (ALU)
  lw               75  (MEM)
  mul              71  (ALU)
  slt              47  (ALU)
  slli             44  (ALU)
  bne              43  (BRANCH)
  srai             30  (ALU)
  sw               17  (MEM)
  mv               16  (PSEUDO)
  nop              16  (PSEUDO)
  slti              5  (ALU)
  beq               5  (BRANCH)
  and               4  (ALU)
  j                 2  (JUMP)
  auipc             1  (ALU)
  ret               1  (JUMP)

生成图表和 HTML 报告
from scratchv.backend.inst_counter import (
    count_instructions_file, generate_chart, generate_html_report
)

counts = count_instructions_file("/tmp/cnn_riscv.s")
generate_chart(counts, "/tmp/inst_chart.png")        # 需要 pip install matplotlib
generate_html_report(counts, "/tmp/inst_report.html", title="CNN Model")

多文件对比
from scratchv.backend.inst_counter import compare_files

# 对比多个汇编文件
result = compare_files(["/tmp/cnn_riscv.s", "/tmp/llvm_output.s"])
# result.counts  → {"file1": Counter(...), "file2": Counter(...)}
# result.diffs   → {"file2": {"ALU": +50, "MEM": -10, ...}}
# result.to_dataframe() → pandas DataFrame

Python API 基础用法
from scratchv.backend.inst_counter import count_instructions_file, format_table

counts = count_instructions_file("/tmp/cnn_riscv.s")
print(format_table(counts))

动手练习

练习 1: 分析 CNN 模型的指令分布

make bench-cnn 生成汇编,统计各类指令占比。哪类最多?为什么?

练习 2: 优化前后对比

对同一段代码,分别统计优化前和优化后的指令分布,计算减少了多少条、哪些类别变化最大。

练习 3: 添加新的指令分类

_OPCODE_CATEGORIES 字典中添加 RISC-V F/D 扩展(浮点)指令的映射。


常见坑
说明
伪指令统计 li a0, 5 统计为 PSEUDO 类别,不会展开为真指令后再统计
matplotlib 依赖 生成图表需要 pip install matplotlib,不是必需功能
指令识别遗漏 某些特殊格式的汇编行可能无法提取操作码(如带复杂寻址模式的指令)

进阶阅读