Pointer equality check does not get optimized for exclusive references

Let's say we have the following function:

pub fn eq(a: &mut u32, b: &u32) -> bool {
    core::ptr::eq(a, b)
}

I would expect for it to get optimized to always return false, but it does not happen in practice (see Compiler Explorer). Is it just a missed optimization in LLVM or is there another reason for that?

3 Likes

For the record, the IR of the (a: &mut u32, b: &mut u32) version is:

define noundef zeroext i1 @eq(ptr noalias noundef readnone align 4 dereferenceable(4) %a, ptr noalias noundef readnone align 4 dereferenceable(4) %b) unnamed_addr {
start:
  %_0 = icmp eq ptr %a, %b
  ret i1 %_0
}

So it seems that LLVM should be able to tell that noalias pointers don't alias.

When the pointers are taken from local variables, it optimizes to false directly.

1 Like

However, there is a deep rabbit hole of pointer equality and mismatches between semantics of llvm.lifetime.start/end and MIR's StorageLive/Dead semantics.

LLVM wants to be able to hardcode not-equal for addresses of local variables, because that's always true in the C abstract machine, but handling of alloca, loops, and stack-reusing optimizations on the real hardware can make local variables share an address, so you can get paradoxes where pointers are both equal and not equal.

3 Likes

Except that it's the exact same IR for fn(&u32, &u32); LLVM noalias (and C restrict) is only concerned with if the pointee is modified. MIR semantics would permit optimizing the comparison to false, but the currently emitted LLVM semantics don't.

2 Likes

Could this optimization cause problems with ZSTs sharing an address?

1 Like

ZSTs already don't have meaningful addresses. [(); !0] has all items at the same address. Except maybe static ZSTs.

More on pointer equality pitfalls: