Various discussions about memory layout and types with larger alignment (generic integers), as well as having a few large-alignment types in our own codebase got me thinking more about alignment and memory layout.
Feature suggestion: Re-align
#[repr(align(512))]
struct Large(u8, u64);
enum Command {
SomethingElse(u8),
LargeVariant(#[realign(1)] Large),
}
impl Large {
#[min_align(1)]
fn access_u8(&self) -> u8 {
self.0
}
#[min_align(8)]
fn access_u64(&self) -> u64 {
self.1
}
#[min_align(8)]
fn write_u64(&mut self) {
self.1 = 5;
}
/// By default only allow this function under the "real"
// alignment (to avoid breaking things).
fn something_that_needs_high_alignment(&self) {
}
}
Instead of forcing the alignment upon the Command
type this would require moving the bytes to an aligned memory location (e.g. on the stack) to call functions that require a higher alignment:
fn do_something(cmd: Command) {
if let Some(value) = cmd {
value.access_u8(); // Allowed because it is already aligned
value.align().access_u64(); // Allowed by moving the required bytes (or the entire thing)
value.aligned(|v| v.access_u64()); // Alternative syntax
value.aligned_mut(|v| v.write_u64()); // Copies the bytes back afterwards (v is &mut Large with alignment of 8 or higher)
value.aligned(|v| v.something_that_needs_high_alignment())
}
}
aligned
and aligned_mut
may need some way to specify which alignment is required for the code running in its closure (or what can be called on it afterwards).
Why would/could this be useful? This allows (at the cost of copying bytes around) to reduce the alignment of a type without having to put it on the heap (though you might still want to do that). It would also allow specifying functions that do not require higher alignment to still be used on lower-alignments (for example when none of the high-alignment functions are needed/used).
Motivation
Let's say I have a type with a large alignment (and no invalid bit patterns) and want to use it in the context of another struct/enum:
#[repr(align(512))]
struct Large(u8);
enum Command {
SomethingElse(u8),
LargeVariant(Large),
}
Due to the alignment restriction Command
has a size of 1024 bytes. The same goes for something simple like Option<Large>
.
The best way to solve this (as far as I know) is to put the type with a large alignment into a Box
:
enum Command2 {
SomethingElse(u8),
LargeVariant(Box<Large>),
}
this reduces the size to 512+16 (512+8 for Option<Box<Large>>
) and the alignment to 8
.
For the code where you need this alignment there is no way around that (as long as size must be a multiple of alignment), but I think there is a better option, especially when you don't need the alignment requirement for everything (in our codebase we need it only in one place):
// Conceptually
enum Command3 {
SomethingElse(u8),
LargeVariant([u8; 512]),
}
This drops the alignment down to 1 and the size to 513 bytes, at the cost of having to copy the memory when the alignment is required.