基于GlobalISel构建LLVM后端时遇“No instructions defined!”错误求助
我正在从零开始为一款RISC风格目标架构编写LLVM后端,已经大致理解LLVM IR转换为MIR、MC最终生成汇编/目标代码的流程,主要参考Colombet所著《LLVM Code Generation》及YouTube上的LLVM开发者会议视频。
当前卡在指令选择流水线的指令选择阶段,项目全程使用GlobalISel。构建该目标架构的LLVM时出现如下错误:
[1/2479] Building XXGenInstrInfo.inc... FAILED: lib/Target/XX/XXGenInstrInfo.inc /home/usr/llvm/build/lib/Target/XX/XXGenInstrInfo.inc ... error: No instructions defined! ... ninja: build stopped: subcommand failed.
XXGenInstrInfo.inc生成失败,之前遇到过其他.inc文件的构建问题,修改对应表生成文件后已解决,但当前错误无法消除。我怀疑是XXInstrInfo.td文件中的模式匹配定义不当导致的。了解到可将SelectionDAG中用于模式匹配的模式导入GlobalISel,但需将SDNode实例转换为通用MachineInstr实例。
目前仅尝试支持目标架构的ADD指令,XXInstrInfo.td中指令及模式匹配的定义如下:
... def ADD : XXInst<(outs GPR:$dst), (ins GPR:$src1, GPR:$src2), "ADD $dst, $src1, $src2">; def : Pat<(add GPR:$src1, GPR:$src2), (ADD GPR:$src1, GPR:$src2)>; def : GINodeEquiv<G_ADD, add>;
另外在XXInstructionSelector.cpp中声明并定义了selectImpl()和select():
bool XXInstructionSelector::select(MachineInstr &I) { // Certain non-generic instructions also need some special handling. if (!isPreISelGenericOpcode(I.getOpcode())) return true; if (selectImpl(I, *CoverageInfo)) return true; return false; }
作为LLVM后端开发新手,请问下一步该尝试什么?
下一步排查与解决步骤
1. 检查XXInst基类的定义是否正确
XXInst作为自定义指令的基类,必须正确继承LLVM的Instruction类(或适配的子类),并且明确设置目标架构命名空间。如果基类未被标记为合法指令类,TableGen会无法识别你定义的ADD指令,进而触发"No instructions defined!"错误。
示例正确的XXInst基类定义:
class XXInst<dag outs, dag ins, string asmstr> : Instruction { let Namespace = "XX"; // 必须与目标架构命名空间一致 let AsmString = asmstr; let OperandList = !con(outs, ins); }
2. 确认XXInstrInfo.td被纳入TableGen构建流程
检查目标架构的CMake配置,确保XXInstrInfo.td被作为输入传递给llvm-tblgen工具生成XXGenInstrInfo.inc。如果该文件未被正确添加到构建依赖,TableGen会找不到任何指令定义。
3. 暂时移除SelectionDAG相关模式
由于你全程使用GlobalISel,传统的Pat模式并非必需,可先注释掉该部分,优先解决指令识别问题:
// 先注释掉SelectionDAG模式,等指令被识别后再处理GlobalISel映射 // def : Pat<(add GPR:$src1, GPR:$src2), // (ADD GPR:$src1, GPR:$src2)>;
GlobalISel依赖GINodeEquiv和选择器代码完成指令匹配,无需依赖SelectionDAG的Pat定义。
4. 验证GPR寄存器类定义合法性
GPR寄存器类必须正确继承LLVM的RegisterClass,且指定正确的目标命名空间、支持的类型和寄存器列表。如果寄存器类定义错误,会导致指令操作数无法被识别,间接让TableGen认为没有合法指令。
示例正确的GPR定义:
def GPR : RegisterClass<"XX", [i32], 32, (add X0, X1, X2, /* ... 其他通用寄存器 */)>;
5. 手动运行llvm-tblgen获取详细报错
直接在命令行执行llvm-tblgen,传入XXInstrInfo.td并指定生成InstrInfo,查看更具体的错误细节:
llvm-tblgen -gen-instr-info /path/to/XXInstrInfo.td -I /path/to/llvm/include -I /path/to/your/target/include
该命令能输出ninja构建日志中被隐藏的具体问题,帮助精准定位指令未被识别的原因。
6. 检查InstructionSelector的依赖关联
确保XXInstructionSelector.cpp正确包含了生成的XXGenInstrInfo.inc,且selectImpl()函数(无论是TableGen自动生成还是手动实现)能正确关联到你的ADD指令。不过当前需优先解决TableGen识别指令的构建错误,再处理后续指令选择逻辑。
内容的提问来源于stack exchange,提问作者Shobhit

