gcc 为什么编译器不自动删除分支?

0tdrvxhp  于 2023-01-09  发布在  其他
关注(0)|答案(1)|浏览(103)

我很好奇编译器是否会自动删除分支,所以我设置了一个简单的案例:

bool testConditionsIf(int i, int j) {
    if (i == 1){
        if (j == 2){
            return true;
        }
        return j == 3;
    }
    return  j == 4;
}

bool testConditionsTernary(int i, int j) {
    return i == 1 ? (j == 2 ? true : j == 3) : j == 4;
}

bool testConditionsNoBranch(int i, int j) {
    return (i == 1 && (j == 2 || j == 3)) || (i != 1 && j == 4);
}

每个函数在语义上都与下一个等价。第二个用三元组而不是if语句重写第一个。第三个通过使用布尔逻辑来处理if语句的两边来重写第二个。有趣的是,使用gcc,前两个产生相同的代码,但第三个不是。前两个是用条件检查和跳转组装的,就像代码写的那样。第三个有一个条件检查1,然后返回每个分支的布尔计算结果。所以我有几个问题:
1.为什么在第三个函数中插入条件检查+跳转,而这已知是CPU减速?
1.为什么不把这三个编译成同一个东西呢?
1.是什么阻止了编译器将前两个编译到第三个?
1.前两个的字节码在i == 1的条件检查之前先做j == 4的条件检查,为什么?如果i == 1,这看起来不是浪费工作吗?
1.如果编译器在跳转之前检查i == 1,为什么在跳转之后再次检查?
1.为什么testConditionsNoBranch中有带-O 0优化标志的分支?编写的代码中没有分支。
https://www.godbolt.org/z/P69TGMsja

6pp0gazn

6pp0gazn1#

三元运算符与if(生成的编译代码)相同,生成的代码可以包含分支。
它带有-O 0优化标志
在禁用优化的情况下测试代码质量毫无意义。
你需要把它转换成一个算术表达式,这个表达式很可能在编译时没有任何分支。

bool testConditionsNoBranch1(int i, int j) {
    return (i == 1) * (j == 2) + (i == 1) * (j == 3) + (i != 1) * (j == 4);
}

它将被编译(使用-Ofast)为

testConditionsNoBranch1(int, int):
        cmp     edi, 1
        sete    cl
        xor     eax, eax
        cmp     esi, 2
        sete    al
        xor     edx, edx
        and     eax, ecx
        cmp     esi, 3
        sete    dl
        and     edx, ecx
        add     eax, edx
        cmp     edi, 1
        setne   cl
        xor     edx, edx
        cmp     esi, 4
        sete    dl
        and     edx, ecx
        add     eax, edx
        setne   al
        ret

相关问题