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 の間で分岐への変換を開始しました。