I was exploring when gcc uses branching vs conditional moves, and found some odd results when using bitwise ANDing on bit 0 of a variable. Specifically, if I do:
int main(int argc, char** argv) {
int y = (2*((~argc)&0x1)) + (1*(argc&0x3));
return y;
}
gcc at -O0 computes argc & 0x1 first, then branches to different basic blocks based on that result. The conversion from arithmetic to branching seems to happen early- dumping the original tree, I get:
;; Function main (null)
;; enabled by -tree-original
{
return (argc & 1) * 2 + ((argc & 1) == 0 ? 3 : 0);
}
return 0;
and the GIMPLE is
int main (int argc, char * * argv)
{
int D.2409;
int iftmp.0;
{
_1 = argc & 1;
_2 = _1 * 2;
_3 = argc & 1;
if (_3 == 0) goto <D.2411>; else goto <D.2412>;
<D.2411>:
iftmp.0 = 3;
goto <D.2413>;
<D.2412>:
iftmp.0 = 0;
<D.2413>:
D.2409 = iftmp.0 + _2;
return D.2409;
}
D.2409 = 0;
return D.2409;
}
This doesn't seem to happen if I don't use both sides of the bitwise AND. It also doesn't seem to happen if I AND with 0x2 instead of 0x1. In those cases, I get purely data processing code, with no jumps. And of course, at optimization levels of 1 or greater, I get optimized jump-free code, but I'm still curious why/how/where GCC converts to branching.
Testing on godbolt, it seems like clang doesn't do this at all, and GCC started doing the conversion to branching between versions 4.1.2 and 4.4.7.