I have the following snippet (minimization of a similar problem encountered in real code):

const N: usize = 32; fn read(block: &[u8; 8 * N]) -> [u64; N] { let (chunks, tail) = block.as_chunks(); assert!(tail.is_empty()); let mut block = [0u64; N]; for i in 0..N { block[i] = u64::from_be_bytes(chunks[i]); } block } 

It gets compiled into the following assembly: Compiler Explorer (RISC-V is used for simplicity, the same issue is present on x86 as well)

As can be clearly seen, the compiler keeps initialization of the buffer (the memset part) despite it being fully overwritten shortly after. Is there a reason for it? On the first glance, it looks like a trivial optimization for the compiler. Interestingly, this memset persists even after the function gets inlined, so it's not some ABI peculiarity.

Note that I know that we can write the function using core::array::from_fn. In my code it can not be used, since next buffer values depend on previously computed buffer values. Also, using it still produces suboptimal code: Compiler Explorer (note the memcpy part)

1 post - 1 participant

Read full topic