AsmPrinter是独立的MachineFunctionPass,仅将已调度和分配寄存器的MachineInstr序列转换为汇编文本,不参与优化决策,需手动实现printInstruction或lowerMachineInstrToMCInst。

汇编输出由 AsmPrinter 类负责,不是指令选择或寄存器分配的副产品
很多人误以为只要把指令选出来、寄存器分好,汇编就自然出来了。其实不然:AsmPrinter 是一个独立的 MachineFunctionPass,它只读取已调度、已分配寄存器的 MachineInstr 序列,不参与任何优化决策。它的唯一职责是把每条 MachineInstr 映射成人类可读(或汇编器可解析)的文本行。
常见错误现象:
- 修改了
XXXInstrInfo.cpp里的指令定义,但.s文件里还是老格式 —— 因为没碰XXXAsmPrinter.cpp - 新增指令在
llc -march=xxx下能生成机器码,却不出现在汇编里 —— 很可能漏写了printInstruction或没注册到XXXMCInstLower
关键点:
-
AsmPrinter不处理 IR、不理解 SDNode,只消费MachineInstr* - 每条指令的文本格式必须在
printInstruction中显式拼接,不能依赖 TableGen 自动生成(除非你用MCInst+MCAsmStreamer路径) - 寄存器名、立即数格式、注释、段指令(如
.text,.data)全靠你在printXXX函数里手写逻辑
MCInst 和 MachineInstr 的映射关系决定汇编是否可重汇编
如果你希望生成的汇编能被 GNU as 或 LLVM llvm-mc 正确反向解析(即支持 round-trip),就必须走 MCInst 路径:在 AsmPrinter 里调用 lowerMachineInstrToMCInst,把 MachineInstr 转成 MCInst,再交给 MCStreamer 输出。
使用场景:
- 目标平台已有成熟汇编语法(如 RISC-V 的
add t0, t1, t2),且需兼容现有工具链 → 必须走MCInst - 仅用于调试或内部仿真,不需要被第三方汇编器识别 → 可直接在
printInstruction里 printf 风格输出
容易踩的坑:
-
MCInst中的 operand 类型(MCOperand::createReg()vscreateImm())必须和 TableGen 中定义的OperandType严格一致,否则llvm-mc -assemble会报invalid operand - 自定义指令的 immediate 范围检查(如 12-bit 符号扩展)必须在
MCInstLowering阶段做,不能等到汇编器报错 -
MCAsmInfo里控制的语法细节(如注释符#还是;、寄存器前缀%还是$)会影响下游工具兼容性,改完要同步测试llvm-mc -filetype=obj
TableGen 生成的汇编模板只覆盖基础指令,自定义指令得手动补全
LLVM 的 *InstrInfo.td 文件通过 AssemblerMatcher 和 DisassemblerDecoder 机制能自动生成部分汇编语法,但这套机制默认只覆盖标准指令集(如 RISC-V 的 ADD, LW)。你加的 MY_CUSTOM_OP 不会自动获得 .td 级别的汇编支持。
实操建议:
- 在
XXXInstrInfo.td中为自定义指令显式添加let AssemblerPredicate = ...和let DecoderNamespace = ...,否则llvm-mc无法识别该助记符 - 如果指令带特殊约束(如要求源寄存器和目标寄存器不能相同),必须在
AsmParser的ParseInstruction实现里做语义检查,TableGen 做不了这事 - 不要指望
def : InstAlias能绕过底层支持 —— 别名只是语法糖,最终仍要落到真实指令的MCInst构造逻辑上
性能影响很小,但调试成本高:没配好 AsmParser 时,llvm-mc 报错信息往往只说 unknown token,得翻 XXXAsmParser.cpp 里的 token 匹配顺序才能定位。
调试汇编输出问题,优先看 llc -debug-only=asmprinter 和 IR → MachineInstr 日志
最有效的调试方式不是改完代码就跑 llc -o test.s,而是让 LLVM 自己“说话”:
-
llc -debug-only=asmprinter:打印AsmPrinter每次调用printInstruction的输入MachineInstr内容,确认指令是否真的传到了这一步 -
llc -print-machineinstrs:输出从 SelectionDAG 合法化后到寄存器分配完成的所有MachineInstr,验证你的指令有没有在早期就被优化掉或替换成其他指令 - 对比
llc -march=xxx -O0和-O2的输出差异 —— 有些指令在优化后会被InstructionCombiningpass 合并或消除,根本不会走到AsmPrinter
容易被忽略的地方:汇编输出是否正确,不只取决于 AsmPrinter,还卡在更上游的指令合法性判定(isLegalToCall)、DAG 合法化(LegalizeTypes)、甚至前端 builtin 是否被正确 lowering 成 intrinsic。所以看到汇编缺失,先查日志里有没有 Skipping instruction: MY_CUSTOM_OP 这类提示。

















