64 位 CPU 上的 ILP32
32 位指针,加上完整的 64 位指令集:内存最多减少 30%,代价是慢几个百分点。 我们的第一个架构是 AArch64,面向已不支持 32 位 Arm 的新核心上的嵌入式 Linux。
什么是 ILP32?
ILP32 程序是 64 位程序:使用 64 位的指令和寄存器,只是内存地址(指针)只用 4 字节存储,而不是 8 字节。
地址变短,数据就变小,程序占用的内存也随之减少。代价是每个程序最多只能使用 4 GB 内存,这对路由器和小板子来说已经足够。
名称的意思是 int、long 和指针都是 32 位;普通的 64 位 Linux 则是 LP64,long 和指针是 64 位。
| 32 位 Arm(armhf) | 普通 64 位(LP64) | ILP32 | |
|---|---|---|---|
| 指令与寄存器 | 32 位 | 64 位 | 64 位 |
| 地址(指针) | 4 字节 | 8 字节 | 4 字节 |
| 内存占用 | 少 | 多 | 少 |
| 新的 Arm 核心能运行吗 | 不能 | 能 | 能 |
| 每个程序可用内存 | 4 GB | 远大于 4 GB | 4 GB |
为什么用 ILP32?
嵌入式设备的内存预算很紧,常见只有 128–512 MB,DRAM 价格又持续上涨。过去在 Arm 上省内存的选择是 32 位 Arm(armhf), 但新的核心(Cortex-A715/A720、Cortex-A520、Cortex-X3 及之后的核心)已经无法运行 AArch32 代码。 ILP32 保留了省内存的好处,又能在这些只支持 AArch64 的核心上运行。
- 指针和数据结构变小:内存占用更少,缓存利用率更高。
- 保留完整的 64 位指令集和寄存器(AArch64 上有 31 个通用寄存器)。
- 内核仍是 64 位,ILP32 和 LP64 程序可以同时运行。
- 64 位
time_t:没有 2038 年问题。
取舍:在同样的编译方式下,ILP32 比 LP64 慢几个百分点,部分原因是 32 位指针的地址运算必须在 4 GiB 处回绕, 编译器有时需要多加指令。详见性能数据。这也不只是 Arm 的想法:Linux 从 2012 年起就有 x86-64 的 x32, Apple 从 2018 年起在 Apple Watch 上使用 arm64_32,RISC-V 的 psABI 也列有实验性的 RV64ILP32 ABI(架构)。
困境
在 AArch64 上,上游的支持一直在退出:Linux 的 ILP32 ABI 从未合入主线,glibc 也没有合入它的移植,GCC 15 把
-mabi=ilp32 标为弃用,Arm 也从 AArch64 的 ABI 文档中移除了 ILP32。我们让整套组件保持可用并一起测试:
内核、C 库、编译器、ILP32Wrt 与 Debian 软件包,全部使用 64 位的 time_t。
我们提供什么
实测
| OpenWrt One(有负载) | LP64 | ILP32 | 差异 |
|---|---|---|---|
| 用户空间内存(PSS) | 38.3 MB | 28.9 MB | -24.6% |
| 可用内存 | 863.6 MB | 872.2 MB | +8.6 MB |
| 速度(四项计时,用 GCC 编译) | 慢 2–11% | ||
同一份源代码、相同的软件包与内核配置,都用 GCC 14.3 编译,只有用户空间的 ABI 不同。 完整结果与测量方法。
现状
- 已在 QEMU(Cortex-A35/A53,以及支持 PAC、BTI、SVE 的
-cpu max)和实机上测试: OpenWrt One 和 Raspberry Pi 3 B+(ILP32Wrt);Raspberry Pi 3 B+、ESPRESSObin、一块 Cortex-A72 板子和一块 Cortex-A35 板子(Debian 13)。 - GCC 不支持 ILP32 的返回地址签名(
pac-ret),打上我们补丁的 clang 22 可以。 - 尚未移植:LLVM 的运行库(compiler-rt、libunwind、libc++),以及 Java、Node.js 等语言运行时。
参与
欢迎到 Discussions 提问、交流想法,分享你自己板子的测试结果。任何人都可以以自己的名义
把我们的补丁提交到上游(请保留原有的 Signed-off-by),也欢迎担任维护者,包括新的架构。
联系方式:[email protected]。