首页 文章

gcc优化?错误?以及它对项目的实际意义

提问于
浏览
11

我的问题分为三个部分

Question 1
考虑下面的代码,

#include <iostream>
using namespace std;

int main( int argc, char *argv[])
{

    const int v = 50;
    int i = 0X7FFFFFFF;

    cout<<(i + v)<<endl;

    if ( i + v < i )
    {
        cout<<"Number is negative"<<endl;
    }
    else
    {
        cout<<"Number is positive"<<endl;
    }

    return 0;
}

不使用特定的编译器优化选项或使用O标志 . 它是基本的编译命令g -o test main.cpp用于形成可执行文件 .

看似非常简单的代码,在SUSE 64位操作系统,gcc版本4.1.2中有奇怪的行为 . 预期输出为“Number is negative”,而仅在SUSE 64位OS中,输出为“Number is positive” .

经过一些分析并对代码进行“解除”后,我发现编译器以下面的格式进行优化 -

  • 由于i在比较的两边是相同的,因此无法在同一表达式中更改,从等式中删除'i' .

  • 现在,比较导致 if ( v < 0 ) ,其中v是常数正数 . 因此在编译期间,else部分cout函数地址被添加到寄存器中 . 没有找到cmp / jmp指令 .

我看到行为仅在gcc 4.1.2 SUSE 10中 . 在AIX 5.1 / 5.3和HP IA64中尝试时,结果如预期 .

Is the above optimisation valid? Or, is using the overflow mechanism for int not a valid use case?

Question 2
现在当我将条件语句从 if (i + v < i) 更改为 if ( (i + v) < i ) 时,行为是相同的,至少我个人不同意,因为提供了额外的大括号,我希望编译器创建一个临时的内置类型变量并进行比较,从而使优化无效 .

Question 3
假设我有一个巨大的代码库,我迁移了我的编译器版本,这样的bug /优化可能会对我的系统行为造成严重破坏 . 从业务角度来看,仅仅因为编译器升级而再次测试所有代码行是非常无效的 .

我认为,出于所有实际目的,这些类型的错误很难捕获(在升级过程中)并且总是会泄漏到 生产环境 现场 .

Can anyone suggest any possible way to ensure to ensure that these kind of bug/optimization does not have any impact on my existing system/code base?


PS:

  • 当从代码中删除const for v时,编译器不会进行优化 .

  • 我相信,使用溢出机制查找变量是否来自MAX - 50值(在我的情况下)是完全没问题的 .


Update(1)
我想要实现什么?变量我将是一个计数器(一种syncID) . 如果我执行离线操作(50操作)然后在启动期间,我想重置我的计数器,为此我检查边界值(重置它)而不是盲目地添加它 .

我不确定我是否依赖于硬件实现 . 我知道0X7FFFFFFF是最大正值 . 我所做的就是,通过增加 Value ,我期待返回值为负值 . 我不认为这个逻辑与硬件实现有任何关系 .

无论如何,谢谢你的意见 .


Update(2)
大多数inpit声明我依赖于溢出检查的较低级别行为 . 我有一个相同的问题,

  • 如果是这种情况,对于unsigned int,如何在下溢或溢出期间验证和重置该值?比如v = 10,i = 0X7FFFFFFE,我想重置i = 9.同样对于下溢?

除非我检查数字的否定性,否则我无法做到这一点 . 所以我的主张是,当一个值添加到MAX_INT时,int必须返回一个负数 .

请让我知道您的意见 .

5 回答

  • 3

    它认为它被认为是编译器中的一个错误 . 当我使用 -Wall -O2 用gcc 4.5编译时,它会发出警告

    警告:假设假设(X c)<X始终为假,则不会发生签名溢出

    虽然你的代码确实溢出了 .

    您可以传递 -fno-strict-overflow 标志以关闭该特定优化 .

  • 11

    您的代码会产生未定义的行为 . 对于有符号整数运算,C和C语言没有"overflow mechanism" . 您的计算溢出了有符号整数 - 行为立即未定义 . 考虑到它的形式"a bug in the compiler or not"位置与尝试分析 i = i++ + ++i 示例没有什么不同 .

    GCC编译器具有基于C / C语言规范部分的优化 . 它被称为“严格溢出语义”或湖泊 . 它基于以下事实:在C中向有符号整数添加正值始终会产生更大的值或导致未定义的行为 . 这立即意味着编译器可以完全自由地假设总和总是更大 . 该优化的一般性质与GCC中也存在的“严格别名”优化非常相似 . 他们都引起了一些抱怨来自海湾合作委员会用户界更“黑客”的部分,其中许多人甚至不怀疑他们在他们的C / C计划中依赖的技巧只是非法黑客攻击 .

  • 0

    Q1:也许,64位实现中的数字确实是正数?谁知道?在调试代码之前,我只需要printf(“%d”,i v);

    Q2:括号只是告诉编译器如何解析表达式 . 这通常以树的形式完成,因此优化器根本看不到任何括号 . 并且可以自由地转换表达式 .

    问题3:这就是为什么,作为c / c程序员,你不能编写承担底层硬件特定属性的代码,例如,int是32位数量的二进制补码形式 .

  • 0

    这条线是什么:

    cout<<(i + v)<<endl;
    

    SUSE示例中的输出?你确定你没有64位整数?

  • 2

    好的,所以这差不多六年了,问题得到了回答 . 我仍然觉得有些内容并没有令我满意,所以我添加了一些评论,希望这对本次讨论的未来读者有所帮助 . (比如当我搜索它时我自己 . )

    • 使用gcc 4.1.2指定的OP没有任何特殊标志 . 我假设缺少 -O 标志等同于 -O0 . 在没有请求优化的情况下,为什么gcc会以报告的方式优化代码?在我看来,这似乎是一个编译器错误 . 我还假设在以后的版本中已经修复了这个问题(例如,一个答案提到了gcc 4.5和 -fno-strict-overflow 优化标志) . 当前的gcc手册页指出 -fstrict-overflow 包含 -O2 或更多 .

    • 在当前版本的gcc中,有一个选项 -fwrapv ,它允许您使用导致OP出现问题的那种代码 . 当然,您必须确保知道整数类型的位大小 . 来自gcc手册页:

    -fstrict-overflow 
    .....
    See also the -fwrapv option. Using -fwrapv means that integer signed overflow 
    is fully defined: it wraps. ... With -fwrapv certain types of overflow are 
    permitted. For example, if the compiler gets an overflow when doing arithmetic 
    on constants, the overflowed value can still be used with -fwrapv, but not otherwise.
    

相关问题