必须实现mctargetasmparser子类:在目标目录建asmparser/子目录,添加继承该类的xxxasmparser.cpp;需重写parseinstruction(拆token为操作数)和matchandemitinstruction(调用tablegen生成的matcher匹配并填充mcinst);须包含get_assembler_header和对应.inc头文件;须在cmakelists.txt中加入源文件,并在末尾实现llvminitializexxxasmparser注册函数。

怎么给 LLVM 后端加 AsmParser(汇编解析器)
LLVM 后端要支持从汇编文本生成 MCInst,必须实现一个 MCTargetAsmParser 子类。这不是可选的“锦上添花”,而是启用 llvm-mc -assemble 或 llc -filetype=asm 输出可重汇编代码的前提。没它,.s 文件直接报错 error: unknown token in expression。
- 在目标目录(如
lib/Target/RISCVN/)下新建AsmParser/子目录 - 创建
RISCVNAsmParser.cpp,继承MCTargetAsmParser,重写ParseInstruction和MatchAndEmitInstruction - 必须包含
#define GET_ASSEMBLER_HEADER和#include "RISCVNGenAsmMatcher.inc"—— 这些由 TableGen 自动生成,漏掉就无法识别指令助记符 - 在
CMakeLists.txt中把该文件加入add_llvm_target的源列表,否则编译时不会链接进去
ParseInstruction 里到底要干啥
ParseInstruction 是汇编解析的入口,负责把当前 token 流(比如 addi x1, x2, 42)拆成操作数对象 MCParsedAsmOperand,并验证语法合法性。它不负责语义匹配,只管“是不是合法字符串”。常见坑:
- 没调用
getLexer().Lex()推进 token,导致无限循环或跳过后续指令 - 对立即数、寄存器名等操作数类型不做范围检查(比如 RISC-V 的
imm[11:0]),留到MatchAndEmitInstruction再报错,用户看到的是模糊的invalid operand for instruction - 忽略大小写处理:RISC-V 规范允许
ADDI和addi,但默认 lexer 区分大小写,需在AsmParser构造函数中调用setParsingInlineAsm(true)并手动 normalize
MatchAndEmitInstruction 怎么把操作数变成 MCInst
这一步才是真正做指令匹配和编码。LLVM 期望你在这里调用 MatchInstructionImpl(由 TableGen 生成的 RISCVNGenAsmMatcher.inc 提供),传入解析好的 MCParsedAsmOperand 数组。关键点:
- 必须把
MCParsedAsmOperand转成目标后端能理解的内部表示(如RISCVNOperand),再喂给 matcher;直接传原始 operand 会触发断言失败 - matcher 返回
Match_Success时,你要调用inst.addOperand()显式填充MCInst的操作数字段,不能只依赖 matcher 的副作用 - 遇到
Match_MnemonicFail或Match_InvalidOperand,必须返回对应错误码,否则llvm-mc会静默跳过该行 - 自定义伪指令(如
.option push)不能走 matcher 流程,得在ParseInstruction里提前拦截并调用getStreamer().emitXXX()
为什么 llvm-mc -arch=riscvn 还是报错 unknown target
即使 AsmParser 编译进去了,llvm-mc 仍不认识你的目标,大概率是注册环节断了。LLVM 用 TargetRegistry 动态绑定,缺一不可:
- 在
RISCVNAsmParser.cpp末尾,必须有extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeRISCVNAsmParser(LLVMOpaqueTarget*)函数 - 该函数内必须调用
RegisterMCAsmParser<riscvnasmparser>(T, X86_64);</riscvnasmparser>—— 注意第二个参数是Triple::riscvn对应的枚举值,不是字符串 - 确保
LLVMInitializeRISCVNTargetInfo、LLVMInitializeRISCVNTarget、LLVMInitializeRISCVNAsmPrinter全部被调用,缺一个都会导致llvm-mc初始化失败
最容易被忽略的是 TableGen 生成时机:改完 .td 文件后必须重新运行 cmake --build . --target RISCVNCommonTableGen,否则 RISCVNGenAsmMatcher.inc 不更新,MatchAndEmitInstruction 永远匹配不到新指令。











