Just an experiment regarding answers given about "... it'll use LEA
":
The following code:
int main(int argc, char **argv)
{
#ifdef USE_SHIFTOR
return (argc << 1 | 1);
#else
return (2 * argc + 1);
#endif
}
will, with gcc -fomit-frame-pointer -O8 -m{32|64}
(for 32bit or 64bit) compile into the following assembly code:
- x86, 32bit:
080483a0 <main>:
80483a0: 8b 44 24 04 mov 0x4(%esp),%eax
80483a4: 8d 44 00 01 lea 0x1(%eax,%eax,1),%eax
80483a8: c3 ret
- x86, 64bit:
00000000004004c0 <main>:
4004c0: 8d 44 3f 01 lea 0x1(%rdi,%rdi,1),%eax
4004c4: c3 retq
- x86, 64bit,
-DUSE_SHIFTOR
:080483a0 <main>:
80483a0: 8b 44 24 04 mov 0x4(%esp),%eax
80483a4: 01 c0 add %eax,%eax
80483a6: 83 c8 01 or $0x1,%eax
80483a9: c3 ret
- x86, 32bit,
-DUSE_SHIFTOR
:00000000004004c0 <main>:
4004c0: 8d 04 3f lea (%rdi,%rdi,1),%eax
4004c3: 83 c8 01 or $0x1,%eax
4004c6: c3 retq
In fact, it's true that most cases will use LEA
. Yet the code is not the same for the two cases. There are two reasons for that:
- addition can overflow and wrap around, while bit operations like
<<
or |
cannot
(x + 1) == (x | 1)
only is true if !(x & 1)
else the addition carries over to the next bit. In general, adding one only results in having the lowest bit set in half of the cases.
While we (and the compiler, probably) know that the second is necessarily applicable, the first is still a possibility. The compiler therefore creates different code, since the "or-version" requires forcing bit zero to 1.