LLVM后端通过TargetLowering::LowerReturn将IR的ret指令映射为寄存器写入或栈操作,严格遵循ABI规定的返回位置;结构体是否走寄存器取决于ABI限制(如X86-64要求≤128位且对齐),否则通过隐式指针传栈;Cpu0等无原生div架构需在performDivRemCombine中按实际使用情况选择性生成CopyFromReg读取LO/HI。

LLVM 后端不直接“实现”返回值传递,而是通过 TargetLowering 和 SelectionDAG 阶段将 IR 中的 ret 指令映射为具体目标平台的寄存器写入或栈操作。关键在于:返回值的物理位置(寄存器 or 栈)由调用约定(calling convention)决定,后端必须严格遵守它。
LLVM IR 的 ret 指令怎么被拆解
IR 层的 ret 只是逻辑标记,不指定存放位置。后端在 LowerReturn()(定义在 TargetLowering::LowerReturn)中处理它:
- 提取
ret的返回值(SDValue)和类型(EVT) - 根据目标 ABI 查询该类型应使用的返回寄存器(如 X86-64 的
%rax/%xmm0,ARM64 的x0/s0) - 若类型过大(如大结构体),则按 ABI 规则生成栈存储指令(如
store到%rdi指向的地址) - 最终生成
CopyToReg节点(寄存器返回)或store+Chain(栈返回)
为什么有些结构体走栈、有些走寄存器
这完全取决于目标平台的 ABI 规定,不是 LLVM 自主决定的。例如:
- X86-64 System V ABI:≤128 位且满足对齐要求的结构体可拆成最多两个整数/浮点寄存器返回;否则必须通过隐式指针参数(
%rdi)传入调用者分配的栈空间 - ARM64 AAPCS64:类似规则,但寄存器上限为 4 个(
x0–x3或s0–s3等) - LLVM 不会自动“优化”返回方式——哪怕结构体只有两个
int,如果 ABI 要求栈传,后端就必须生成store到%rdi指向的地址
Cpu0 后端里除法返回值的特殊处理
像 Cpu0 这类无原生 div 指令的架构,SDIVREM 节点会同时产出商和余数,但硬件只提供一对寄存器(LO/HI)。这时后端必须显式控制哪些值被真正传出:
-
performDivRemCombine中检查N->hasAnyUseOfValue(0)(商是否被使用) - 仅当商被使用时,才插入
CopyFromReg从LO读取,并替换原 DAG 中的SDValue(N,0) - 同理处理
HI寄存器对应余数 - 没被使用的值不会生成任何
CopyFromReg,也就不会出现在最终机器码中
最易被忽略的一点:返回值传递不是孤立行为。它必须与调用方的接收逻辑(LowerFormalArguments 对隐式返回缓冲区的处理、寄存器清空策略)和指令选择阶段的合法化(getOperationAction(ISD::RET, ...))完全对齐,否则会产生 ABI 不兼容的二进制。

















