新增指令后必须补的回归测试包括三类:基本生成测试(验证指令能否被选中并输出正确汇编)、非法化测试(验证非法操作数触发错误而非静默降级)、优化穿透测试(确认中端优化不误删或重排该指令),还需覆盖调试信息继承、多架构属性开关、globalisel合法化及特殊寄存器处理等交叉验证点。

新增指令后必须补的回归测试类型
LLVM 后端新增一条自定义或标准扩展指令(比如 RISC-V 的 vadd.vv 或自定义加密指令 crypto.aesenc),光让 llc 能编译过去远远不够。不补回归测试,下一次 GlobalISel 重构、InstructionSelection 优化或寄存器分配改动,就可能悄无声息地把你的指令漏掉、降级成软实现,甚至触发断言崩溃。
test/CodeGen 目录下要覆盖的最小测试集
所有新增指令必须在 llvm/test/CodeGen 下添加对应架构子目录的测试文件(如 llvm/test/CodeGen/RISCV/),且至少包含以下三类用例:
-
基本生成测试:用
define void @test() { ... call void @llvm.foo() ... ret void }或简单 C 函数(通过clang -S -emit-llvm生成 IR)验证该指令能否被合法选中,输出汇编中出现目标指令字面量(如addi或crypto.aesenc),并用; CHECK: crypto.aesenc断言 - 约束与非法化测试:显式构造非法操作数(如对非 16 字节对齐地址用
crypto.aesenc),验证后端正确返回CannotSelect或触发report_fatal_error,而非静默降级 -
优化穿透测试:在 IR 中插入该指令的输入/输出依赖链(如
%x = call i32 @llvm.crypto.aesenc(i32 %a, i32 %b)后接add i32 %x, 1),确认instcombine、gvn等中端优化不会误删或重排它——尤其当指令有隐式副作用(如修改状态寄存器)时
调试信息与调试体验不能丢
如果新指令参与调试(比如用于内联汇编或 intrinsic 实现),必须在测试中验证 DebugLoc 是否被正确继承。常见错误是:SelectionDAG 节点生成时没调用 getMachineNode(..., dl, ...),导致生成的 MInstr 没带源位置,GDB 单步时直接跳过整段代码。补测试时加一行 ; CHECK: [[LINE:[0-9]+]]:{{.*}}crypto.aesenc 并用 llvm-dwarfdump 检查 .debug_line 段是否含对应行号。
容易被忽略的交叉验证点
很多人只测 llc -march=riscv32,但忘了验证:
– llc -march=riscv64 -mattr=+your-ext 下是否仍有效(属性开关逻辑是否正确定义在 RISCVSubtarget.h)
– opt -passes=irtranslator,legalizer,cauldron(若启用 GlobalISel)是否能走通新指令的 LegalizationRule
– 如果指令涉及特殊寄存器(如 RISC-V 的 csr),要确认 LiveVariables 和 RegisterCoalescer 不会把它当成普通 GPR 处理











