Just a quick observation that I think might be a bug. If I have the following function:
const assert = @import("std").debug.assert;
export fn find_msb(val: usize) usize {
// assert(val != 0);
var v = val;
var r:usize = 0;
while (v != 0) : (v >>= 1) {
r += 1;
}
return r;
}
It compiles to: [1]
example.find_msb:
lzcnt rcx, rdi
mov eax, 64
sub rax, rcx
test rdi, rdi
cmove rax, rdi
ret
find_msb = example.find_msb
- Count the leading zeros
- Subtract that from 64
- Check if the answer is zero (i.e. 64 leading zeros… function has zero as an input)
- If so, overwrite the result with zero
- Return the result.
(Trunk doesn’t do 3 & 4)
Seeing this, I thought I’d add an assert (commented out above) to say the input is never zero, so no need to check for it. I got this result:
example.find_msb:
xor eax, eax
.LBB0_1:
inc rax
shr rdi
jne .LBB0_1
ret
find_msb = example.find_msb
It’s now a basic loop and doesn’t use the lzcnt instruction[2]. Why would reducing the set of valid inputs defeat an optimisation?