> Their implementation is littered with uses of "unsafe" which means its almost impossible for the compiler to eliminate the bounds checks.
Does `unsafe` actually impede optimization in this way? I thought it just disabled certain type checks and error messages but didn't affect anything on the LLVM level.
You're essentially right. The grandparent may be meaning that they're using a lot of raw pointers (which requires unsafe), and raw pointers means less aliasing information, and so less precise optimisations. This can affect bounds checks, because a raw pointer could potentially alias the length field of a slice or vector, and so LLVM has to be conservative around writes to them.
Are their uses of unsafe necessary? After briefly looking over the implementation it seems like they could just be using references in a bunch of places. Take for example:
let queue = &mut self.rx_queues[queue_id as usize];
rx_index = queue.rx_index;
last_rx_index = queue.rx_index;
for i in 0..num_packets {
let desc = unsafe { queue.descriptors.add(rx_index) as *mut ixgbe_adv_rx_desc };
let status =
unsafe { ptr::read_volatile(&mut (*desc).wb.upper.status_error as *mut u32) };
I've got no idea. One would have to understand the code fairly deeply to tell that. For instance, are the volatile reads/writes meant to be atomic ones (which could be done safely) or are they truly volatile?
Does `unsafe` actually impede optimization in this way? I thought it just disabled certain type checks and error messages but didn't affect anything on the LLVM level.