gcc/g++和clang:有条件的错误优化
我有一个有关GCC和CLANG代码优化的问题。这件代码显示出奇怪的行为。 arr
初始化为0
在main
中,sizef(int)
in arr_ctor
和变为0
在arr_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 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您正在问两个问题,
被删除了,因为指针上的算术无法产生nullptr。 它就会被视为
因此,一旦所有赌注都关闭, null上的算术是ub。 printf可能会发生,可能不会
You are asking 2 questions
It gets eliminated becuase arithmetic on a pointer can never yield nullptr. So it gets treated as
Arithmetic on NULL is UB, once you do that all bets are off. printf might happen, might not