LLVM新后端最简注册需三步:定义MyArch.td目标、用RegisterTarget注册getTheMyArchTarget()、在CMakeLists.txt中add_subdirectory(MyArch);随后可绕过指令选择输出空汇编,再通过RET指令实现首条汇编生成。

LLVM 新后端最简注册:只让 llc 不报错
想让 LLVM 认识一个新架构,第一步不是写指令、不是配寄存器,而是让它能“看见”这个目标。否则 llc -march=myarch 会直接报 error: invalid target 'myarch'。这一步只需三类文件:目标描述(.td)、注册入口(TargetInfo.cpp 和 TargetMachine.cpp)和 CMake 声明。
关键点在于 TargetRegistry::RegisterTarget 调用必须在链接时可见,且 getTheMyArchTarget() 符号不能被 strip。常见坑是忘了在 CMakeLists.txt 中把新目录加进 add_subdirectory,或者 TargetInfo.cpp 没实现 getTheMyArchTarget() 返回非空指针。
- 新建
llvm/lib/Target/MyArch/MyArch.td,至少包含def MyArch : Target; - 在
MyArchTargetInfo.cpp中调用RegisterTarget,传入getTheMyArchTarget()地址 - 在
MyArchTargetMachine.cpp中定义getTheMyArchTarget()并返回全局静态实例 - 确保
llvm/lib/Target/CMakeLists.txt包含add_subdirectory(MyArch)
生成空汇编输出:绕过指令选择与寄存器分配
注册成功后,llc -march=myarch test.ll 仍会失败,典型错误是 SelectionDAG has not been legalized yet 或 no instruction selector。此时不需要实现完整指令选择,只要让 SelectionDAG 阶段不崩溃即可。最轻量做法是继承 TargetLowering 并重写 LowerOperation,对所有操作返回原样节点(Op),再在 TargetMachine 中禁用寄存器分配。
这样做的效果是:IR → SelectionDAG(未 legal)→ 直接 emit 空汇编(或占位符)。它不产生有效代码,但能验证后端骨架是否加载、pass 流水线是否跑通。性能无关紧要,目标是快速获得可调试的上下文。
- 在
MyArchTargetLowering.h中继承TargetLowering,构造函数调用setOperationAction(ISD::ADD, MVT::i32, Expand)等全部基础操作为Expand - 重写
LowerOperation,统一返回Op,避免触发未实现的 lowering 逻辑 - 在
MyArchTargetMachine.cpp的createPassConfig中,跳过addRegAllocPass和addISelPass - 用
llc -march=myarch -O0 -filetype=asm test.ll验证是否输出空.text段
添加最简指令选择:从 ret void 开始
空输出只能帮你确认流程,真正迈出第一步是让一条最简单的 IR 指令生成对应汇编。推荐从 ret void 入手,因为它不依赖参数传递、栈帧、寄存器映射等复杂机制。你需要的最小集是:MyArchInstrInfo.td 定义一条 RET 指令、MyArchAsmPrinter.cpp 实现汇编打印、以及 MyArchISelDAGToDAG.cpp 把 ISD::RET 映射过去。
注意 TableGen 生成的 *GenInstrInfo.inc 必须被正确包含,否则 getRetOpcode() 会返回 0 导致断言失败。另一个易错点是 AsmPrinter 的 printInstruction 没覆盖 MyArch::RET 枚举值,导致 fallback 到未实现分支而 abort。
-
MyArchInstrInfo.td中定义def RET : InstMyArch<...> { let Pattern = [(ret)]; } -
MyArchAsmPrinter.cpp的printInstruction加case MyArch::RET: O -
MyArchISelDAGToDAG.cpp的select函数中匹配Node->getOpcode() == ISD::RET,然后CurDAG->getMachineNode(MyArch::RET, ...) - 运行
llc -march=myarch -O0 test.ll,输入 IR 为define void @f() { ret void },应输出ret
为什么 statepoint 和 relocation 在初期可以忽略
Native Image 的 LLVM 后端文档明确提到,Graal 生成的 bitcode 会包含 statepoint 指令用于 GC 安全点,且最终对象文件需支持重定位(relocation)才能链接。但对纯 LLVM 新后端开发而言,这两项不是启动障碍——它们属于“运行时兼容性”问题,而非“编译链路打通”问题。
你可以在没有 statepoint lowering 的情况下生成汇编(Graal 不会发给你的后端带 statepoint 的 bitcode,除非你主动启用 GC 支持);重定位则由 MCObjectStreamer 和 MCExpr 层处理,只要不用 -filetype=obj 就不涉及。换句话说:先搞定 -filetype=asm,再考虑 obj;先支持裸函数返回,再支持 GC 安全点。
-
statepoint是 JVM/GC 语义相关,纯 C/Rust 前端生成的 IR 默认不含它 - 重定位只在生成目标文件(
.o)时生效,汇编输出(.s)无需处理符号重定位 - GraalVM Native Image 的 LLVM 后端要求你提供
statepointlowering,但那是 Graal+LLVM 协同场景,非标准 LLVM 后端开发起点 - 初期用
clang --target=myarch不可行,因为 clang 还不知道myarch,所以必须从.ll输入开始
tblgen 没重新生成 .inc 文件就直接编译。每次改完 .td,记得跑一遍 make -j4(或 ninja),观察日志里有没有 Building MyArchGen*.inc 这类行。没有,说明 TableGen 步骤根本没触发。

















