Hello! Now i am reading book Asynchronous Programming in Rust (Carl Fredrik Samson)
The chapter is dedicated to the implementation of green threads/fibers. Next code fragment is taken from this book.
I'm interested in a single line: let sb_aligned = (stack_bottom as usize & !15) as *mut u8;
How can this be read?
Author describes this: this line of code essentially rounds our
memory address down to the nearest 16-byte-aligned address. If it’s already 16 byte-aligned,
it does nothing.
I'm new to Rust, and certainly haven't written unsafe code.
What does this cast stack_bottom as usize & !15 mean? And in what context are the & and ! operators used, how do it affect memory alignment?
// I did not list all the elements used in this code fragment, so as not to clutter,
// and commented out the corresponding lines
const SSIZE: isize = 48;
fn main() {
// let mut ctx = ThreadContext::default();
let mut stack = vec![0_u8; SSIZE as usize];
unsafe {
let stack_bottom = stack.as_mut_ptr().offset(SSIZE);
let sb_aligned = (stack_bottom as usize & !15) as *mut u8;
// std::ptr::write(sb_aligned.offset(-16) as *mut u64, hello as u64);
// ctx.rsp = sb_aligned.offset(-16) as u64;
// gt_switch(&mut ctx);
}
}
! is bitwise negation so 15 (0b0000_0000_0000_0111) turns into 0b1111_1111_1111_1000 assuming 32 bit pointers. & is the bitwise-and operator. Since stack_bottom is being and-ed against !15, it'll end up setting the bottom 3 bits to 0 which has the effect of rounding stack_bottom down to the nearest multiple of 16.
Okay.. Do I understand correctly that in this way we reset the lower 4 bits of the pointer,
(You seem to have mistyped !15 as 0b...1000), moving our pointer towards smaller addresses.
And it is precisely the 4 least significant bits that need to be reset so that the final number (address) is a multiple of 16, which is related to 16-byte-aligned, which was our goal (Another question is why we wanted this alignment) ?
Something like ptr & !15 looks very unusual, it doesn’t even look like pointer arithmetic..
So after dynamic languages, this is a bit weird =)
Because book doesn't support use of AVX and AVX512?
From ELF x86-64-ABI psABI: The end of the input argument area shall be aligned on a 16 (32 or 64, if __m256 or __m512 is passed on stack) byte boundary. In other words, the stack needs to be 16 (32 or 64) byte aligned immediately before the call instruction is executed. Once control has been transferred to the function entry point, i.e. immediately after the return address has been pushed, %rsp points to the return address, and the value of (%rsp + 8) is a multiple of 16 (32 or 64).
These are completely new topics for me... Alignment, ABI, CPU registers and related platform-specific subtleties.
Regarding 16-byte alignment, the book says: “One more thing to note is that the stack alignment on x86-64 is 16 bytes. Just remember this for later,” and also that this is a requirement of the C language ABI.
And the corresponding code fragment is written for the x86-64 and OS Linux platform
Well… that's very sensible thing to do for such a book. If you would read essay named What Every Programmer Should Know About Memory you would know why this alignment requirement exists (and more), but that's 100+ pages of pretty complicated topics and thus the only sensible thing for a book like this to say is precisely this: “Linux on x86-64 requires that, trust us, it's needed”.