The disjoint
flag on or
is actually quite new to LLVM, so you might be interested in [RFC] Add or disjoint flag - IR & Optimizations - LLVM Discussion Forums
My understanding is that it's useful because of a confluence of two things:
- In general, LLVM would rather use the weaker operation (bitor) than the stronger one (add), since checks like "is this bit set?" are easier without carries.
- However, some CPU operations exist with addition but not with bitwise ops, like LEA.
*goes to try to make a demo*
Ah, here we go. These two compile differently in x86
#[no_mangle]
pub unsafe fn demo1(p: *const u8, a: usize, b: usize) -> *const u8 {
p.add((4*a) + b)
}
#[no_mangle]
pub unsafe fn demo2(p: *const u8, a: usize, b: usize) -> *const u8 {
p.add((4*a) | b)
}
giving https://rust.godbolt.org/z/f93rPh9EP
demo1:
lea rax, [rdi + 4*rsi]
add rax, rdx
ret
demo2:
lea rax, [4*rsi]
or rax, rdx
add rax, rdi
ret
So the better one here depends on knowing about your target's instructions, which is something that we ideally wouldn't make people know. And if you used disjoint_or
here they could compile to the same thing.
Thus this method to say "look, it can be implemented with or
, add
, or xor
; whichever is convenient".
Do I have any good examples of where I'd actually use this? TBH no. It's very important for LLVM to preserve knowledge rather than needing to re-prove it every time, but using it for things like (a << 16) | b
in surface rust wouldn't be needed because (assuming small enough inputs) LLVM will prove that itself and then keep it around later for the backend to use.
But I do think it would be worth having in nightly to try to find those things, like how we have exact_div
.