llvm后端不直接输出栈展开或调试信息,而是生成供运行时和调试器使用的元数据:需在machineinstr/mcinst中注入lsda描述符(依赖abi及ehstreamer)和dbg_instr_ref(依赖debug-instr-number与getdebugloc),并确保emitprologue/epilogue、cfi指令、invoke支持、寄存器分配时的debug值替换等环节正确实现。

LLVM 后端本身不直接“输出”栈展开或调试信息,而是生成供运行时(如 libcxxabi)和调试器(如 gdb、lldb)使用的元数据和指令标记。关键在于:你得让后端在生成 MachineInstr 和 MCInst 时,正确注入两类东西——LSDA 描述符(用于栈展开)和 DBG_INSTR_REF / debug-instr-number(用于变量位置追踪)。
怎么让 LLVM 后端生成 LSDA 和 .eh_frame 段
栈展开依赖目标平台的异常处理 ABI(如 DWARF EH 或 ARM EHABI),LLVM 通过 TargetLowering::getExceptionPointerRegister()、getExceptionSelectorRegister() 等钩子控制行为。实际输出由 AsmPrinter pass 完成:
- 确保你的后端在
TargetFrameLowering中正确实现emitPrologue()和emitEpilogue(),并在其中调用MCCFIInstruction系列 API(如addDefCfaOffset、addOffset)来生成 CFI 指令 - 启用
-exceptions或-unwind-tables编译选项(Clang 默认不开),否则SelectionDAGBuilder会跳过landingpad相关节点,AsmPrinter也不会 emit.eh_frame - LSDA 内容由
EHStreamerpass 构建,它读取MachineFunction::getLandingPads()和每个invoke的landingpad指令;如果你的后端没支持invoke指令选择(比如漏了ISD::INVOKE的LowerInvoke实现),LSDA 就是空的 - RISC-V 等无原生异常寄存器的目标,需在
TargetLowering中返回0表示不支持exception-pointer,否则EHStreamer可能 crash
为什么加了 -g 还看不到变量值:DBG_INSTR_REF 没生效
-g 只是开关,真正决定调试信息质量的是后端是否在每条 relevant MachineInstr 上写入 debug-instr-number,并让 DBG_VALUE 指令引用它。常见断点:
-
MachineInstr::setDebugInstrNumber()必须在指令调度前(即MachineScheduler之前)被调用;如果寄存器分配器(RegAlloc)做了 coalescing 或 splitting,但没调用MachineFunction::substituteDebugValuesForInst(),DBG_INSTR_REF就会指向已删除的指令 - 自定义指令(如 RISC-V builtin)若没在
SelectionDAG阶段打上SDNode::setDebugLoc(),后续所有MachineInstr都不会带debug-instr-number -
LiveDebugVariablespass 已被弃用;新流程靠DBG_INSTR_REF+MachineInstr::getDebugLoc()联动,但前提是 IR 层的dbg.value指令没被 DCE 掉——检查是否误开了-O2且没加-grecord-gcc-switches
调试时 gdb 显示 “optimized out”,但代码没开优化
这往往不是编译器问题,而是后端在寄存器分配阶段破坏了指令引用链。典型表现是 DBG_VALUE %0, $x, !123 对应的 %0 是一个 virtual register,而该 register 在 MachineInstr 中被 split 成多个物理寄存器,但 substituteDebugValuesForInst() 没被触发:
- 确认你的
RegisterInfo实现中,eliminateFrameIndex()是否保留了MachineInstr::getDebugLoc();某些后端在改写 frame index 时直接 clone 指令却丢掉了 debug loc - 检查
TargetPassConfig::addPreEmitPass()是否注册了DebugVariableManager(RISC-V 后端默认没开,需手动加) - 用
llc -debug-only=debug-variables观察日志里有没有Failed to find def for debug value—— 有就说明某条DBG_VALUE引用的MachineInstr被删了,且没做 substitution
最易被忽略的一点:LSDA 和调试信息都依赖于 MachineFunction 的 getMMI().getLandingPads() 和 getDebugValueMap() 在 code emission 前非空。如果某个 pass(比如你写的 custom instruction selector)提前清空了这些容器,后面所有输出都会静默失效,且无 warning。











