0

gcc が分岐と条件付き移動を使用する時期を調査していたところ、変数のビット 0 でビットごとの AND 演算を使用すると、奇妙な結果が見つかりました。具体的には、私が行う場合:

int main(int argc, char** argv) {
    int y = (2*((~argc)&0x1)) + (1*(argc&0x3)); 
    return y;
}

gcc at -O0 は最初に argc と 0x1 を計算し、次にその結果に基づいて別の基本ブロックに分岐します。算術演算から分岐への変換は、元のツリーを早期にダンプするように思われます。次のようになります。

;; Function main (null)
;; enabled by -tree-original


{
  return (argc & 1) * 2 + ((argc & 1) == 0 ? 3 : 0); 
}
return 0;

そしてGIMPLEは

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;
}

ビットごとの AND の両側を使用しない場合、これは発生しないようです。また、0x1 ではなく 0x2 と AND を使用しても発生しないようです。そのような場合、ジャンプのない純粋なデータ処理コードを取得します。もちろん、最適化レベルが 1 以上の場合、ジャンプのない最適化されたコードが得られますが、GCC が分岐に変換される理由/方法/場所にまだ興味があります。

Godbolt でテストすると、clang はこれをまったく行わないようで、GCC はバージョン 4.1.2 と 4.4.7 の間で分岐への変換を開始しました。

4

0 に答える 0