ILP32 for 64-bit CPUs
32-bit pointers with the full 64-bit instruction set: up to 30% less memory, at a cost of a few percent of speed. Our first architecture is AArch64, for embedded Linux on cores that no longer run 32-bit Arm.
What is ILP32?
An ILP32 program is a 64-bit program: it uses the 64-bit instructions and registers. Only memory addresses
(pointers) are stored in 4 bytes instead of 8. Shorter addresses mean smaller data, so the program needs less
memory. The limit is 4 GB of memory per program, which is plenty for routers and small boards. The name means
that int, long and pointers are 32 bits; normal 64-bit Linux is LP64, with 64-bit
long and pointers.
| 32-bit Arm (armhf) | normal 64-bit (LP64) | ILP32 | |
|---|---|---|---|
| Instructions and registers | 32-bit | 64-bit | 64-bit |
| Address (pointer) size | 4 bytes | 8 bytes | 4 bytes |
| Memory use | low | high | low |
| Runs on recent Arm cores | no | yes | yes |
| Memory per program | 4 GB | far more | 4 GB |
Why ILP32?
Embedded devices run on tight memory budgets, often 128–512 MB, and DRAM keeps getting more expensive. The classic memory-saving choice on Arm was 32-bit Arm (armhf), but recent cores (Cortex-A715/A720, Cortex-A520, Cortex-X3 and later) no longer run AArch32 code. ILP32 keeps the memory savings and runs on these AArch64-only cores.
- Smaller pointers and data structures: less memory, better cache use.
- The full 64-bit instruction set and registers stay (on AArch64: 31 general-purpose registers).
- The kernel stays 64-bit; ILP32 and LP64 programs run side by side.
- 64-bit
time_t: no year-2038 problem.
The trade-off: ILP32 is a few percent slower than LP64 for the same build, partly because address arithmetic on 32-bit pointers must wrap at 4 GiB, so the compiler sometimes needs extra instructions. See the benchmarks. The idea is not Arm-only: Linux has had x32 for x86-64 since 2012, Apple has shipped arm64_32 on Apple Watch since 2018, and the RISC-V psABI lists an experimental RV64ILP32 ABI (architectures).
The challenge
On AArch64, upstream support has been retreating: the Linux ILP32 ABI was never merged, glibc never
merged its port, GCC 15 deprecated -mabi=ilp32, and Arm removed ILP32 from its AArch64 ABI
documents. We keep the whole stack working and tested together: kernel, C libraries, compilers,
ILP32Wrt and Debian packages, with a 64-bit time_t throughout.
What we provide
Measured
| OpenWrt One, loaded | LP64 | ILP32 | change |
|---|---|---|---|
| User-space memory (PSS) | 38.3 MB | 28.9 MB | -24.6% |
| Free memory | 863.6 MB | 872.2 MB | +8.6 MB |
| Speed (four timings, built with GCC) | 2–11% slower | ||
Same tree, packages and kernel configuration, both built with GCC 14.3; only the user-space ABI differs. Full results and method.
Status
- Tested in QEMU (Cortex-A35/A53, and
-cpu maxwith PAC, BTI and SVE) and on hardware: the OpenWrt One and a Raspberry Pi 3 B+ (ILP32Wrt); a Raspberry Pi 3 B+, an ESPRESSObin, a Cortex-A72 board and a Cortex-A35 board (Debian 13). - GCC has no return address signing (
pac-ret) for ILP32; clang 22 with our patches does. - Not ported yet: the LLVM runtimes (compiler-rt, libunwind, libc++) and language runtimes such as Java and Node.js.
Get involved
Questions, ideas and results from your own boards are welcome in
Discussions. Anyone may take our patches upstream under their own name
(please keep the original Signed-off-by), and maintainers are welcome, also for new
architectures. Contact: [email protected].