1

使用 GCC6 和下面的代码片段,这个测试

if (i > 31 || i < 0) {

false,并且这个 printf 被执行

printf("i > 31 || i < 0 is FALSE, where i=%d", i);

并产生这个非常奇怪的输出(GCC6):

我 > 31 || i < 0 是FALSE,其中i=32 /* GCC6 的奇怪结果!!!*/

而使用 GCC4 我得到:

我 > 31 || i < 0 为,其中i=32 /* GCC4 结果正常 */

看起来完全没问题。

怎么会这样??

代码片段(损坏的遗留代码!):

static int check_params(... input parameters ...) {
    /* Note that value can be 0 (zero) */
    uint32_t value = ....
    int i;

    i = __builtin_ctz(value);
    if (i > 31 || i < 0) {
        printf("i > 31 || i < 0 is true, where i=%d", i);
        /* No 1 found  */
        return 0;
    } else {
        printf("i > 31 || i < 0 is FALSE, where i=%d", i);
    }
    return i;
}

根据有关 GCC 内置函数的文档,必须避免调用 __builtin_ctz(0) :

内置函数:int __builtin_ctz (unsigned int x) 返回 x 中尾随 0 位的数量,从最低有效位位置开始。如果 x 为 0,则结​​果为 undefined

所以很明显,解决编码错误的方法是在调用 __builtin_ctz(value) 之前简单地检查值。这是清楚和理解的。

我可以停在那里并转移到其他主题......但是,我仍然不明白我怎么可能(使用损坏的代码)获得以下输出:

我 > 31 || i < 0 是FALSE,其中i=32 /* GCC6 的奇怪结果!!!*/

奇怪的 GCC6 优化还是什么?

以防万一它很重要:

Cross-compiler: arm-linux-gcc
Architecture: -march=armv7-a

任何的想法?

4

4 回答 4

6

除非有未定义的行为,__builtin_ctz否则总是会返回一个介于 0 和 31 之间的数字,而 GCC 知道这一点。因此,检查i > 31 || i < 0将始终是错误的(再次禁止未定义的行为)并且可以优化掉。

如果您查看生成的程序集,您会发现该条件根本没有出现在代码中(当时的情况也没有)。

于 2018-06-15T15:42:36.633 回答
4

未定义的行为并不意味着“值将是任意的”。这意味着编译器实际上可以做任何它想做的事情。在这种情况下,编译器似乎能够静态验证,只要value不是 0i将始终介于 0 和 31 之间。所以它甚至不用为 then 子句生成代码。

你只是幸运的恶魔没有从你的鼻子里出来。

另请参阅:未定义的行为可能导致时间旅行过早的沮丧为什么未定义的行为可能会调用从未调用的函数,以及许多其他关于 UB 的讨论

于 2018-06-15T15:42:59.723 回答
1

编译器假定未定义行为不会发生。它可以做出这样的假设,因为如果违反了约束并且行为未定义,则任何结果都是可能的,包括由错误假设导致的结果。

如果没有Undefined Behaviour,则i不能为负数或大于31。在此基础上,if可以在编译时优化语句中的条件。

printf无法预测实际打印的值,因此它实际上会调用printf任何i发生的情况。在这种情况下,它恰好是 32,但它可能是任何东西。

于 2018-06-15T15:45:32.423 回答
0

C 标准指出,实现将未定义行为视为邀请编译器以记录的环境特征进行行为的邀请是很常见的,并且基本原理指出,将行为分类为 UB 旨在导致提供超越特性的高质量实现市场需要时由标准规定的那些。尽管如此,从他们的行为来看,一些编译器供应商认为,让编译器寻找聪明的方法来利用自由来处理某些无意义的情况比让他们在实际时采用合理的约束行为(例如,使用 __builtin_ctz( )以未指定的方式在 CPU 所做的任何事情或产生一些任意值之间进行选择)。

我个人相当怀疑,允许编译器做任何事情而不是产生一个值或执行任何自然后果的 CPU 操作,从而促进任何“优化”的价值将接近允许程序员假设的价值在 CPU 本身不做任何奇怪事情的平台上,该操作只会产生一个未指定的结果,并且没有副作用。尽管如此,gcc 的作者似乎认为要求程序员添加额外的源代码来处理可以在没有额外机器代码的情况下处理的情况会以某种方式提高“效率”。

于 2018-06-15T20:26:09.567 回答