llvm后端不直接支持复杂寻址模式,需在selectiondag或globalisel阶段显式分解为add/shl等基础操作,再由目标后端通过complexpattern或手动匹配合并为原生指令。

LLVM后端如何处理复杂寻址模式(如基址+变址×尺度+位移)
LLVM 后端本身不“支持”复杂寻址模式——它要求你显式建模、分解并匹配。所谓“复杂寻址”,比如 x86-64 的 [rax + rbx * 4 + 12],在 LLVM IR 层面根本不存在;它必须在 SelectionDAG 或 GlobalISel 阶段被拆解为合法的 ADD、SHL、LOAD 组合,再由目标后端决定是否将其中一部分合并回一条原生指令。
为什么不能直接在 IR 里写复杂寻址
LLVM IR 是平台无关的、SSA 形式的低阶中间表示,它只定义了基础运算(add、mul、load、getelementptr),不包含任何目标相关寻址语法。所有“寻址逻辑”都推迟到后端指令选择阶段处理。
-
getelementptr只计算字节偏移,不生成地址表达式;结果仍是抽象指针值 - 最终
load/store指令的操作数只能是单个寄存器或常量,不能是嵌套表达式 - 后端需通过
TargetLowering::getAddrModeArguments或自定义ComplexPattern捕获可折叠的地址成分
在 SelectionDAG 中启用地址折叠的关键步骤
要让后端把 add (shl x, 2), y, 12 合并成一条带 SIB 字节的 mov eax, [rbx + rsi * 4 + 12],你得在目标描述(.td)和 C++ 代码中协同配置:
- 在
MyArch.td中用complex_pattern定义合法地址结构,例如:def addr : ComplexPattern<i64>;</i64> - 在
MyArchISelLowering.cpp实现SelectAddr:识别ADD、SHL、OR等组合,并返回DAG.getNode(ISD::ADD, ...)或直接构造MyArchISD::ADDR节点 - 在
MyArchInstrInfo.td中为 load/store 指令添加(ops addr:$addr)操作数,并用let AddedComplexity = -20提高匹配优先级 - 确保
TargetLowering::isLegalAddressingMode返回true对应尺度/位移范围(如 x86 支持scale in [1,2,4,8])
容易踩的坑:GlobalISel 下地址模式更难控制
如果你用的是 GlobalISel(而非传统 SelectionDAG),事情会更琐碎:没有 ComplexPattern,也没有自动的 SelectAddr 回调。你必须在 MyArchInstructionSelector.cpp 中手动检查 G_ADD、G_SHL、G_CONSTANT 的 DAG 结构,并用 constrainSelectedInstRegOperands 强制寄存器类匹配。一旦尺度因子不是 2 的幂,或者位移超出 32 位有符号范围,LLVM 就会静默退回到多条指令序列——不会报错,但性能掉得明显。
真正麻烦的地方不在建模,而在于测试覆盖:不同优化级别(-O0 vs -O2)、不同 getelementptr 嵌套深度、是否涉及 alloca 或全局变量取址,都会触发不同的地址生成路径。漏测一种组合,就可能在线上跑出两倍长的地址计算指令。











