llvm的codemodel决定全局地址空间寻址边界,直接影响指令选择与重定位:small模型限±2gb直接寻址,medium支持pc-relative(如x86-64的rip-relative或arm64的adrp+add),large则全用64位寄存器加载(如movabs),代价是体积与性能开销增大;选错将导致链接失败、地址截断或指令降级。

Code Model决定全局地址空间的寻址能力边界
LLVM的CodeModel不是可有可无的配置项,它直接参与指令选择和重定位生成,最终约束生成代码能合法访问的地址范围。选错会导致链接失败、运行时地址截断或无法生成lea/mov类指令——尤其在大内存程序或嵌入式小地址空间场景下,这个问题会立刻暴露。
small/medium/large三种模型的实际行为差异
LLVM默认用CodeModel::Small,但它只保证所有全局符号(函数、数据)能在±2GB内被直接寻址。一旦目标平台实际地址超出这个范围(比如x86-64上加载到0x100000000以上),必须显式切换:
-
CodeModel::Medium:允许代码段与数据段分别位于任意位置,但要求PC-relative寻址仍可用(x86-64上依赖lea或mov+ RIP-relative);ARM64上对应adrp+add组合 -
CodeModel::Large:放弃任何PC-relative假设,所有全局引用都走完整的64位寄存器加载(如x86-64的movabs),代价是多1–2条指令、多几个字节编码、更差的缓存局部性 - 不指定时,后端可能按目标默认推导(如AArch64默认
Medium,X86默认Small),但Clang命令行不透传该选项,需通过-mcode-model=显式控制
常见错误现象与调试线索
当你看到以下任一提示,大概率是CodeModel没对齐:
- 链接时报
relocation truncated to fit: R_X86_64_PC32 against symbol—— 这是Small模型下试图用32位相对偏移跳转到远地址 - 汇编输出里出现大量
movq $0x..., %rax; callq *%rax而非直接callq foo—— 后端被迫降级为Large路径,但你没意识到 - 启用
-mcmodel=large后性能下降明显,且objdump -d显示大量movabs—— 说明确实触发了全量地址加载逻辑
验证方式:用llc -march=x86-64 -mcode-model=medium处理一个含远地址引用的IR,对比small输出的指令序列差异,重点看call、lea、mov的操作数宽度。
Pass开发中容易忽略的耦合点
写自定义MachineFunctionPass时,别假设所有全局符号都能用MCOperand::createExpr配MO_PC_RELATIVE。实际是否允许,取决于当前MachineFunction::getTarget().getCodeModel()返回值。如果Pass要插入跳转或取地址指令,必须检查:
- 目标符号是否在
CodeModel::Small范围内(可通过getOffsetOf()+ 当前函数地址粗略估算) - 是否已启用
SupportsStaticAddrTaken等后端特性标志 - 避免在
Large模型下硬编码MO_Immediate——应改用MO_ConstantPoolIndex或MO_BlockAddress交由AsmPrinter处理
最隐蔽的问题是:同一份IR,在不同CodeModel下生成的Machinelnstr可能完全不同,但IR本身看不出区别。调试时务必确认TargetMachine构造时传入的CodeModel参数,而不是只看IR文本。











