gcc/g++和clang:有条件的错误优化

发布于 2025-01-21 22:17:15 字数 3068 浏览 1 评论 0原文

我有一个有关GCC和CLANG代码优化的问题。这件代码显示出奇怪的行为。 arr初始化为0main中,sizef(int) in arr_ctor和变为0arr_resize中。因此,条件不应执行。使用-O2编译时,将消除条件并fprintf执行。但是,将MSVC与 /O2一起使用时,并且代码正常。

#include <stdio.h>

int arr_resize(int* arr)
{
    arr--;

    if(arr != nullptr)                      // this conditional shouldn't be removed 
        fprintf(stderr, "arr = %p\n", arr); //

    return 0;
}

int arr_ctor(int* arr)
{
    arr++;
    arr_resize(arr);

    return 0;
}


int main()
{
    int* arr = {};

    arr_ctor(arr);

    return 0;
}

命令行:

gcc main.cpp -o test_gcc -o2 -wall -wextra

clang main.cpp -o test_clang -o2 -wall -wextra

输出(GCC):

arr =(nil)

输出(clang):

arr =(nil)

输出(MSVC):无输出

组装显示该条件是在GCC和Clang中消除的,但在MSVC中呈现。

GCC(-O2):

<...>

arr_resize:

    subq    $8, %rsp

    leaq    -4(%rdi), %rdx
    movq    stderr(%rip), %rdi
    xorl    %eax, %eax
    leaq    .LC0(%rip), %rsi
    call    fprintf@PLT
    xorl    %eax, %eax
    addq    $8, %rsp

    ret

<...>

clang(-O2):

<...>

arr_resize:

    pushq   %rax

    leaq    -4(%rdi), %rdx
    movq    stderr@GOTPCREL(%rip), %rax
    movq    (%rax), %rdi
    leaq    .L.str(%rip), %rsi
    xorl    %eax, %eax
    callq   fprintf@PLT
    xorl    %eax, %eax
    popq    %rcx

    retq

<...>

MSVC(/o2):

<...>

int arr_resize(int *)
    push    rbx
    sub     rsp, 32
    mov     rbx, rcx
    sub     rbx, 4
    je      SHORT $LN4@arr_resize
    mov     ecx, 2
    call    __acrt_iob_func
    mov     rcx, rax
    lea     rdx, OFFSET FLAT:`string'
    mov     r8, rbx
    call    fprintf
$LN4@arr_resize:
    xor     eax, eax
    add     rsp, 32
    pop     rbx
    ret     0

<...>

命令行:

gcc main.cpp -o test.s -s -o2 -wall -wextra -fno -exceptions

clang main.cpp -o test.s -s -o2 -wall -wextra -fno -exceptions

msvc仅在 /o2上在godbolt上测试,因为我没有。 Clang和GCC在Godbolt和我的PC上进行了测试。

为了进行比较,没有优化的GCC:

<...>    

arr_resize:
.LFB0:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    %rdi, -8(%rbp)
    subq    $4, -8(%rbp)
    cmpq    $0, -8(%rbp)
    je  .L2
    movq    stderr(%rip), %rax
    movq    -8(%rbp), %rdx
    leaq    .LC0(%rip), %rcx
    movq    %rcx, %rsi
    movq    %rax, %rdi
    movl    $0, %eax
    call    fprintf@PLT
.L2:
    movl    $0, %eax
    leave
    ret

<...>

编译器:

GCC版本11.2.0(Godbolt上的11.2.0)

Clang版本13.0.1(Godbolt上的14.0.0)

MSVC版本19.31在Godbolt上

是一个错误还是我错过了什么?

I have a question about gcc and clang code optimization. This piece of code shows strange behavior. arr initialized as 0 in main, becomes sizeof(int) in arr_ctor and becomes 0 in arr_resize. So, conditional shouldn't be executed. When compiled with -O2 conditional gets eliminated and fprintf executes. However, when using MSVC with /O2 conditional stays presented and code works fine.

#include <stdio.h>

int arr_resize(int* arr)
{
    arr--;

    if(arr != nullptr)                      // this conditional shouldn't be removed 
        fprintf(stderr, "arr = %p\n", arr); //

    return 0;
}

int arr_ctor(int* arr)
{
    arr++;
    arr_resize(arr);

    return 0;
}


int main()
{
    int* arr = {};

    arr_ctor(arr);

    return 0;
}

Command line:

gcc main.cpp -o test_gcc -O2 -Wall -Wextra

clang main.cpp -o test_clang -O2 -Wall -Wextra

Output (gcc):

arr = (nil)

Output (clang):

arr = (nil)

Output (MSVC): no output

Assembly shows that conditional was eliminated in GCC and Clang, but presented in MSVC.

GCC (-O2):

<...>

arr_resize:

    subq    $8, %rsp

    leaq    -4(%rdi), %rdx
    movq    stderr(%rip), %rdi
    xorl    %eax, %eax
    leaq    .LC0(%rip), %rsi
    call    fprintf@PLT
    xorl    %eax, %eax
    addq    $8, %rsp

    ret

<...>

Clang (-O2):

<...>

arr_resize:

    pushq   %rax

    leaq    -4(%rdi), %rdx
    movq    stderr@GOTPCREL(%rip), %rax
    movq    (%rax), %rdi
    leaq    .L.str(%rip), %rsi
    xorl    %eax, %eax
    callq   fprintf@PLT
    xorl    %eax, %eax
    popq    %rcx

    retq

<...>

MSVC (/O2):

<...>

int arr_resize(int *)
    push    rbx
    sub     rsp, 32
    mov     rbx, rcx
    sub     rbx, 4
    je      SHORT $LN4@arr_resize
    mov     ecx, 2
    call    __acrt_iob_func
    mov     rcx, rax
    lea     rdx, OFFSET FLAT:`string'
    mov     r8, rbx
    call    fprintf
$LN4@arr_resize:
    xor     eax, eax
    add     rsp, 32
    pop     rbx
    ret     0

<...>

Command line:

gcc main.cpp -o test.s -S -O2 -Wall -Wextra -fno-exceptions

clang main.cpp -o test.s -S -O2 -Wall -Wextra -fno-exceptions

MSVC was tested only on godbolt with /O2, because I don't have it. Clang and GCC were tested on godbolt and on my PC.

For comparison, GCC without optimizations:

<...>    

arr_resize:
.LFB0:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $16, %rsp
    movq    %rdi, -8(%rbp)
    subq    $4, -8(%rbp)
    cmpq    $0, -8(%rbp)
    je  .L2
    movq    stderr(%rip), %rax
    movq    -8(%rbp), %rdx
    leaq    .LC0(%rip), %rcx
    movq    %rcx, %rsi
    movq    %rax, %rdi
    movl    $0, %eax
    call    fprintf@PLT
.L2:
    movl    $0, %eax
    leave
    ret

<...>

Compilators:

gcc version 11.2.0 (11.2.0 on godbolt)

clang version 13.0.1 (14.0.0 on godbolt)

MSVC version 19.31 on godbolt

Is it a bug or am I missing something?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

吃不饱 2025-01-28 22:17:15

您正在问两个问题,

  • 为什么要删除代码,
  • 为什么执行的printf

被删除了,因为指针上的算术无法产生nullptr。 它就会被视为

if(42 == 42) // ie always true

因此,一旦所有赌注都关闭, null上的算术是ub。 printf可能会发生,可能不会

You are asking 2 questions

  • why does the code get eliminated
  • why is that printf executed

It gets eliminated becuase arithmetic on a pointer can never yield nullptr. So it gets treated as

if(42 == 42) // ie always true

Arithmetic on NULL is UB, once you do that all bets are off. printf might happen, might not

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文