Rust resolves long-standing 128-bit integer alignment inconsistencies with C, improving FFI compatibility and performance while introducing memory usage trade-offs.
Rust 1.77 and 1.78 bring significant changes to the alignment of 128-bit integers (i128/u128) on x86-32 and x86-64 architectures, resolving a years-long inconsistency with C that has implications for FFI compatibility and performance.
The Problem: Inconsistent Alignment
For years, Rust has had a fundamental mismatch with C regarding how 128-bit integers are aligned in memory. While C (via Clang and GCC) aligns __int128 types to 16-byte boundaries, Rust's LLVM backend had these types hardcoded to 8-byte alignment. This created serious interoperability issues when passing these types between Rust and C code.
To understand why this matters, consider how memory alignment works. Every data type has a size (how much space it occupies) and an alignment (which memory addresses it can be placed at). For optimal performance, most 64-bit systems prefer 8-byte integers to be stored at addresses that are multiples of 8. However, 128-bit integers are different - the x86-64 System V ABI specifies they must be 16-byte aligned.
This discrepancy meant that Rust and C couldn't reliably share data structures containing 128-bit integers, leading to subtle bugs and crashes in FFI code.
The Technical Details
The issue manifested in two ways:
1. Storage Alignment: Rust was using 8-byte alignment for i128/u128 while C used 16-byte alignment. This meant that composite types containing these integers would have different memory layouts between the two languages.
2. Calling Convention: LLVM (used by both Rust and Clang) had a bug where it would incorrectly split 128-bit integers across registers and stack when passing them as function arguments, violating the ABI specification.
The Solution
The fix came in multiple stages:
- LLVM 18 (October 2023): Harald van Dijk's patch corrected the alignment to 16 bytes, and Nikita Popov fixed the calling convention issue
- Rust 1.77: Matthew Maurer implemented manual alignment correction similar to Clang's workaround, ensuring compatibility even with older LLVM versions
- Rust 1.78: Ships with bundled LLVM 18, providing full compatibility with C implementations
Performance Implications
Interestingly, the alignment change revealed performance benefits. Since the ABI specifies 16-byte alignment for performance reasons, the fix actually improved compiler performance - Rust's compiler makes heavy use of 128-bit integers for working with integer literals. However, increased alignment can lead to more memory usage in some cases, as composite types may require additional padding to maintain proper alignment.
Compatibility Matrix
The changes create a complex compatibility landscape:
| Compiler 1 | Compiler 2 | Status |
|---|---|---|
| Rust ≥ 1.78 with LLVM 18 | GCC (any) | Fully compatible |
| Rust ≥ 1.78 with LLVM 18 | Clang ≥ 18 | Fully compatible |
| Rust ≥ 1.77 with LLVM ≥ 18 | GCC (any) | Fully compatible |
| Rust ≥ 1.77 with LLVM ≥ 18 | Clang ≥ 18 | Fully compatible |
| Rust ≥ 1.77 with LLVM ≥ 18 | Clang < 18 | Storage compatible, calling bug |
| Rust ≥ 1.77 with LLVM < 18 | GCC (any) | Storage compatible, calling bug |
| Rust < 1.77 | GCC (any) | Incompatible |
| Rust < 1.77 | Clang (any) | Incompatible |
| GCC (any) | Clang ≥ 18 | Fully compatible |
| GCC (any) | Clang < 18 | Storage compatible with calling bug |
What This Means for Developers
Most Rust developers won't notice these changes directly. However, if you're working with FFI code that uses 128-bit integers, you should:
- Use
align_of::<i128>()instead of assuming alignment - this ensures your code works correctly across all Rust versions - Pay attention to
improper_ctypeslints - these warnings exist for good reasons and help catch potential ABI issues - Test thoroughly when upgrading - especially if your code mixes Rust with C/C++ libraries
Starting with Rust 1.77, it's reasonably safe to experiment with 128-bit integers in FFI code, with full certainty arriving in 1.78. The Rust team is cautiously considering lifting the improper_ctypes lint in future versions, but wants to avoid breaking existing code that might be using older LLVM versions.
The fix represents years of collaborative work across the Rust and LLVM communities, demonstrating how even seemingly small ABI details can have far-reaching implications for systems programming languages.

Featured image: Suzanne D. Williams on Unsplash

Comments
Please log in or register to join the discussion