targetmachine::create 返回 nullptr 的根本原因是目标未注册:必须实现继承自 targetmachine 的子类,定义并调用 llvminitializexxxtargetinfo 和 llvminitializexxxtarget 初始化函数,将其注册到 target registry,且链接对应库、triple 匹配、datalayout 一致,四者缺一不可。

直接回答:必须实现一个继承自 TargetMachine 的具体子类,并注册到 LLVM 的 Target Registry 中,否则 TargetMachine::create 会返回空指针。
为什么 TargetMachine::create 总是返回 nullptr
这是最常见也最容易卡住的起点。LLVM 不会自动发现你写的 XXXTargetMachine 类——它靠静态注册机制(通过 extern "C" void LLVMInitializeXXXTargetInfo() 等函数)把目标信息注入全局 registry。如果没调用初始化函数,TargetMachine::create 就找不到对应目标,只能返回 nullptr。
常见错误现象:
- 编译通过,但运行时
TargetMachine::create(...)返回nullptr,后续调用getInstrInfo()等直接 crash - 链接时提示 undefined reference 到
LLVMInitializeXXXTargetInfo或LLVMInitializeXXXTarget
解决办法:
- 确保在
XXXTargetInfo.cpp中定义了LLVMInitializeXXXTargetInfo(注册 TargetInfo) - 在
XXXTarget.cpp中定义了LLVMInitializeXXXTarget(注册 Target + TargetMachine) - 在主程序或 pass 初始化处显式调用这两个函数(例如在
main()开头或registerPasses()里) - 链接时包含你的
libLLVMXXXCodeGen.a(或对应动态库),不能只链接libLLVMCore
XXXTargetMachine 必须实现哪些方法
不是“可选”,而是 LLVM 后端流水线硬依赖的访问入口。缺任何一个,后续指令选择、寄存器分配都会失败。
核心方法列表(以 RISCV 为例,你的目标名替换 RISCV):
-
getTargetLowering()→ 返回RISCVTargetLowering*,处理 IR 到 SelectionDAG 的语义映射 -
getInstrInfo()→ 返回RISCVInstrInfo*,提供指令编码、延迟、副作用等元信息 -
getRegisterInfo()→ 返回RISCVRegisterInfo*,描述寄存器文件、调用约定、压栈规则 -
getFrameLowering()→ 返回RISCVFrameLowering*,生成函数 prologue/epilogue -
getDataLayout()→ 返回 constDataLayout&,必须与你目标的 ABI 严格一致(如指针大小、对齐)
注意:getDataLayout() 返回的 DataLayout 字符串必须和你目标实际一致,比如 RISC-V 64-bit 是 "e-m:e-p:64:64-i64:64-i128:128-n32-S128";写错会导致 getPointerSizeInBits() 返回异常值,后续所有地址计算出错。
如何触发机器码生成(不是汇编)
很多人以为构造出 TargetMachine 就能直接 emit 二进制,其实 LLVM 默认只做汇编输出。要生成真正的机器码(MCInst → ObjectFile),必须走 MC 层流程。
关键路径:
- 用
TargetMachine::addPassesToEmitFile添加 emit pass,传入CGFT_ObjectFile(不是CGFT_AssemblyFile) - 确保
AsmInfo、MCContext、MCStreamer和MCObjectWriter全部正确初始化(通常由TargetMachine自动提供,但需检查getMCAsmInfo()非空) - 链接时必须包含
libLLVMObject、libLLVMMC、libLLVMMCParser,否则MCObjectWriter构造失败
典型错误:
- 调用
addPassesToEmitFile时传CGFT_AssemblyFile却期望得到二进制 —— 输出的是 .s 文件 - 忘记在 CMakeLists.txt 中
target_link_libraries加上LLVMObject等 MC 相关库,导致 runtime 报 “no object writer registered”
最容易被忽略的初始化顺序问题
LLVM 的 Target 注册是静态的,但初始化函数调用顺序受链接器符号解析影响。如果你的目标代码放在插件(.so/.dll)里,或使用了 LTO,LLVMInitializeXXXTarget 可能根本没被调用。
验证方式(调试时加):
dbgs() <p>如果输出为 0,说明 registry 是空的;如果输出包含你的目标名但 <code>create</code> 仍失败,大概率是 <code>Triple</code> 字符串不匹配(比如传了 <code>"riscv64-unknown-elf"</code>,但你的 Target 只注册了 <code>"riscv32"</code>)。</p> <p>真正起作用的从来不是“写了类”,而是“让 LLVM 在 runtime 看见它”。注册、链接、Triple 匹配、DataLayout 一致性——四者缺一不可。</p>











