In all 4 examples, the last 4 instructions are superfluous!
1. the optimizer should be aware that both "n <<= 1;" and "n += n;"
yield an even number, and thus a following "n += 1;" can't
generate a carry.
More general: after "n <<= x;" addition of any value less than
1 << x can't generate a carry.
2. the optimzier should also be aware that addition of 1 to an even
number yields an odd number, and thus a following "n |= 1;" is
More general: after "n <<= x;", the addition of any value less
than 1 << x is equal to a logical or with the same value.
3. last, it should never create "mov rcx, rdx" followed by an
inverse "mov rdx, rcx".
I also wonder why a shld is created here: at least for "n += n;"
I expect a more straightforward
add rax, rax
adc rdx, rdx
PS: of course GCC x86-32 exhibits the same flaws with int64_t!