我有一个关于gcc和clang代码优化的问题。这段代码表现出奇怪的行为。arr
在main
中初始化为0
,在arr_ctor
中初始化为sizeof(int)
,在arr_resize
中初始化为0
。条件语句不应该被执行。当使用-O2编译时,条件被消除并执行fprintf
。但是,当使用带有/O2条件的MSVC时,条件保留并且代码可以正常工作。
#include <stdio.h>
int arr_resize(int* arr)
{
arr--;
if(arr != nullptr) // this conditional shouldn't be removed
fprintf(stderr, "arr = %pn", 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
输出(gcc):clang main.cpp -o test_clang -O2 -Wall -Wextra
输出(叮当声):arr = (nil)
arr = (nil)
输出(MSVC):无输出
汇编显示条件在GCC和Clang中被取消,但在MSVC中出现。
GCC (- 02):
<...>
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
<...>
叮当声(- 02):
<...>
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只在godbolt上用/O2测试过,因为我没有。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
<...>
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
这是一个bug还是我错过了什么?
你问了两个问题
- 为什么代码被淘汰
- 为什么要执行printf
它被消除了,因为指针上的算术运算永远不会产生nullptr。所以它被当作
if(42 == 42) // ie always true
NULL上的算术是UB,一旦你这样做,所有的赌注都取消了。Printf可能发生,也可能不发生