Why does GCC -O0 convert bitwise AND and multiply to branching?

96 Views Asked by At

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.

0

There are 0 best solutions below