我正在探索 gcc 何时使用分支与条件移动,并在变量的位 0 上使用按位与运算时发现了一些奇怪的结果。具体来说,如果我这样做:
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 的两侧,这似乎不会发生。如果我使用 0x2 而不是 0x1,它似乎也不会发生。在这些情况下,我得到纯粹的数据处理代码,没有跳转。当然,在优化级别为 1 或更高时,我得到了优化的无跳转代码,但我仍然很好奇 GCC 转换为分支的原因/方式/位置。
在 Godbolt 上测试,似乎 clang 根本不这样做,并且 GCC 开始在 4.1.2 和 4.4.7 版本之间转换为分支。