【问题标题】:Optimization levels in gcc changing c program behaviourgcc 中的优化级别改变了 c 程序的行为
【发布时间】:2012-02-07 16:25:54
【问题描述】:

在 gcc 中使用不同优化级别编译此代码时,我看到了我不期望的行为。

函数测试应该用 1 填充 64 位无符号整数,将它们向左移动 shift_size 位,并将低 32 位作为 32 位无符号整数返回。

当我使用 -O0 编译时,我得到了我期望的结果。

当我使用 -O2 编译时,如果我尝试移动 32 位或更多位,我不会这样做。

事实上,如果我在 x86 上通过大于或等于位宽的移位来移位 32 位整数,我得到的结果正是我所期望的,这是仅使用移位大小的 5 个低位的移位。

但是我正在移动一个 64 位数字,所以移动

我认为这是我理解中的错误,而不是编译器中的错误,但我无法弄清楚。

我的机器: gcc (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5 i686-linux-gnu

#include <stdint.h>
#include <stdio.h>
#include <inttypes.h>

uint32_t test(unsigned int shift_size) {
    uint64_t res = 0;
    res = ~res;
    res = res << shift_size; //Shift size < uint64_t width so this should work
    return res; //Implicit cast to uint32_t
}

int main(int argc, char *argv[])
{
    int dst;
    sscanf(argv[1], "%d", &dst); //Get arg from outside so optimizer doesn't eat everything
    printf("%" PRIu32 "l\n", test(dst));
    return 0;
}

用法:

$ gcc -Wall -O0 test.c 
$ ./a.out 32
0l
$ gcc -Wall -O2 test.c 
$ ./a.out 32
4294967295l

gcc -S -Wall -O0 test.c

gcc -S -Wall -O2 test.c

【问题讨论】:

  • 你能把你的编译器生成的汇编代码的相关部分也贴出来吗? (-S 用于 gcc)
  • FWIW 它使用 gcc 4.2.1 在从 -O0-O3 的所有优化级别上为我提供了正确的结果 (0l),所以我怀疑你可能 发现了一个 gcc 错误。
  • 嗯,我的代码在两个优化级别都可以正常工作...(gcc 版本 4.5.0 20100604,openSUSE 11.3 (x86_64))
  • @Alex:如果您尝试使用带有 -O2 -m32 的 non-LLVM gcc 4.2.1,那么您应该会看到它(这就是我正在使用的)
  • 这是一个已确认的错误:gcc.gnu.org/bugzilla/show_bug.cgi?id=51821

标签: c optimization gcc bit-manipulation shift


【解决方案1】:

"%u"(或"%lu")和uint32_t 不一定兼容。 试试

    #include <inttypes.h>

    //printf("%ul\n", test(dst));
    printf("%" PRIu32 "l\n", test(dst));

使用"%u"(或"%lu")说明符打印uint32_t 值可以调用未定义行为。

【讨论】:

  • 好调用,uint32_t 可能不是无符号整数。
  • @dreamlax:我认为关键是 OP 错误地使用了“%ul”而不是“%lu”,最终被 printf 当作“%u”。使用 PRIu32 消除了使用错误说明符的可能性。
【解决方案2】:

我能够重现这一点。这是-O2生成的代码的相关位:

    movl    $-1, %eax
    movl    $-1, %edx
    sall    %cl, %eax
    xorl    %edx, %edx
    testb   $32, %cl
    cmovne  %eax, %edx
    cmovne  %edx, %eax    ; This appears to be the instruction in error.
                          ; It looks as though gcc thought that %edx might still
                          ; be zero at this point, because if the shift count is
                          ; >= 32 then %eax should be zero after this.

这是与-O0 等效的位:

    movl    -16(%ebp), %eax
    movl    -12(%ebp), %edx
    shldl   %cl,%eax, %edx
    sall    %cl, %eax
    testb   $32, %cl
    je      L3
    movl    %eax, %edx
    xorl    %eax, %eax         ; correctly zeros %eax if shift count >= 32
L3:
    movl    %eax, -16(%ebp)
    movl    %edx, -12(%ebp)

编译器是:

i686-apple-darwin11-gcc-4.2.1 (GCC) 4.2.1 (Apple Inc. build 5666) (dot 3)

感谢您发布您的 gcc -S 输出。我看了看,虽然略有不同,但关键部分的错误与我在机器上看到的错误相同。

【讨论】:

    【解决方案3】:

    它看起来可能对我来说是一个 32 位特定的编译器错误。使用问题中的代码和 gcc 4.2.1,只要我使用 gcc -m32 -O2 ... 编译,我就可以重现该错误。

    但是,如果我添加一个调试 printf:

    uint32_t test(unsigned int shift_size) {
        uint64_t res = 0;
        res = ~res;
        res = res << shift_size; //Shift size < uint64_t width so this should work
        printf("res = %llx\n", res);
        return res; //Implicit cast to uint32_t
    }
    

    然后问题就消失了。

    下一步是查看生成的代码以尝试识别/确认错误。

    【讨论】:

    • 作为一种临时解决方法,也许可以打印到/dev/null:FILE *null; null = fopen("/dev/null","w"); fprintf(null,"res = %llx\n", res); fclose(null);
    • 作为一种临时解决方法,使res volatile 也可以正常工作,并且不需要任何文件操作。
    【解决方案4】:

    它看起来像一个错误。我的猜测是编译器已经折叠了最后两行:

    res = res << shift_size
    return (uint32_t)res;
    

    进入:

    return ((uint32_t)res) << shift_size;
    

    后者现在已明确定义为 32 或更大。

    【讨论】:

      【解决方案5】:

      我不记得C99怎么说了,但是在gcc中,uint32_t似乎包含至少 32位,并且可能包含更多,所以当你优化它时,它使用64位var,这在 64 位机器上速度更快。

      【讨论】:

      • 不, uint32_t (如果可用)正好是 32 位。 (uint_fast32_t 或 uint_least32_t 可能更大,但不是 uint32_t)
      • uint64_t 并非在所有情况下都比 uint32_t 快:如果您在 intel/amd64 机器上具有 64 位地址空间,则它在数组中的索引速度更快,因为 16 位和 32 位索引必须在 8 位和64 位是该汇编语言中的合法偏移量。但是如果您使用更大的位大小,则乘法和除法会更慢。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-12-19
      • 2021-08-28
      • 2023-03-30
      • 1970-01-01
      • 1970-01-01
      • 2016-01-01
      相关资源
      最近更新 更多