gcc-if语句优化
gcc是否优化代码,例如:gcc-if语句优化,c,if-statement,gcc,optimization,C,If Statement,Gcc,Optimization,gcc是否优化代码,例如: if(cond) func1() func2() if(cond) func3() 如果cond是常数?另外,有什么好的资源可以学习更多关于C优化的知识?(所以我不必再问这样愚蠢的问题了)通常是的。公共子表达式是在函数范围内计算的,因此如果(x*x/y>z+z)出现两次,x、y和z不变,则表达式将只计算一次。但是,编译器需要知道,例如,变量的地址没有被获取并传递给某个子例程。通常是。公共子表达式是在函数范围内计算的,因此如果(x*x/y>z+z)出现
if(cond)
func1()
func2()
if(cond)
func3()
如果cond是常数?另外,有什么好的资源可以学习更多关于C优化的知识?(所以我不必再问这样愚蠢的问题了)通常是的。公共子表达式是在函数范围内计算的,因此如果(x*x/y>z+z)出现两次,x、y和z不变,则表达式将只计算一次。但是,编译器需要知道,例如,变量的地址没有被获取并传递给某个子例程。通常是。公共子表达式是在函数范围内计算的,因此如果(x*x/y>z+z)出现两次,x、y和z不变,则表达式将只计算一次。但是,编译器需要知道,例如,变量的地址没有被获取并传递给某个子例程。是的,如果编译器证明这是允许的,并且认为这是值得的,那么编译器肯定会对此进行优化 我尝试了最新版本的
gcc
、icc
和clang
,他们都对上述代码进行了优化,有效地:
if (cond) {
func1();
func2();
func3();
} else {
func2();
}
也就是说,他们复制了对func2()
的调用以启用此优化。下面是一个典型的编译代码示例,来自gcc:
cmp edi, 33
je .L7
xor eax, eax
jmp func2
.L7:
sub rsp, 8
xor eax, eax
call func1
xor eax, eax
call func2
xor eax, eax
add rsp, 8
jmp func3
当然,这样的优化并不能得到保证——编译器可能会认为它不值得,而且它可能依赖于特定于编译器的启发式和编译选项。例如,当使用-Os
时(与我上面使用的-O2
相反),gcc
不再重新排列它,而是在编写时或多或少地编译它,使用两条cmp
指令:
cmp edi, 33
jne .L5
xor eax, eax
call func1
.L5:
xor eax, eax
call func2
cmp edi, 33
jne .L4
xor eax, eax
jmp func3
.L4:
ret
另一方面,clang
和icc
在复制调用时,通过一次比较继续编译它
您可以使用所有这些功能。是的,如果编译器证明这是允许的,并且认为这是值得的,那么他们肯定会对此进行优化
我尝试了最新版本的gcc
、icc
和clang
,他们都对上述代码进行了优化,有效地:
if (cond) {
func1();
func2();
func3();
} else {
func2();
}
也就是说,他们复制了对func2()
的调用以启用此优化。下面是一个典型的编译代码示例,来自gcc:
cmp edi, 33
je .L7
xor eax, eax
jmp func2
.L7:
sub rsp, 8
xor eax, eax
call func1
xor eax, eax
call func2
xor eax, eax
add rsp, 8
jmp func3
当然,这样的优化并不能得到保证——编译器可能会认为它不值得,而且它可能依赖于特定于编译器的启发式和编译选项。例如,当使用-Os
时(与我上面使用的-O2
相反),gcc
不再重新排列它,而是在编写时或多或少地编译它,使用两条cmp
指令:
cmp edi, 33
jne .L5
xor eax, eax
call func1
.L5:
xor eax, eax
call func2
cmp edi, 33
jne .L4
xor eax, eax
jmp func3
.L4:
ret
另一方面,clang
和icc
在复制调用时,通过一次比较继续编译它
您可以打开所有这些功能。是的。
您可以通过查看编译器的汇编代码来检查编译器正在做什么。我已经创建了一个小的C程序来使用,确保它具有一些可变性,这样优化器就不会只对所有内容进行常量折叠
#include <stdio.h>
void func1(const char input) {
printf("func1: %c\n", input);
}
void func2(const char input) {
printf("func2: %c\n", input);
}
void func3(const char input) {
printf("func3: %c\n", input);
}
int main() {
char input = (char)getchar();
if(input == 'Y') {
func1(input);
}
func2(input);
if(input == 'Y') {
func3(input);
}
}
请注意,这是没有优化的。您可以在不必阅读大量汇编的情况下找到比较,查找89,它是Y
的ASCII十进制表示形式
LCFI10:
subq $16, %rsp
call _getchar
movb %al, -1(%rbp)
cmpb $89, -1(%rbp)
jne L5
movsbl -1(%rbp), %eax
movl %eax, %edi
call _func1
L5:
movsbl -1(%rbp), %eax
movl %eax, %edi
call _func2
cmpb $89, -1(%rbp)
jne L6
movsbl -1(%rbp), %eax
movl %eax, %edi
call _func3
L6:
movl $0, %eax
leave
这是两个if块的死记硬背的翻译。您可以看到两个cmpb$89,-1(%rbp)
调用,指示正在进行两次比较
现在进行优化:gcc-S-O3 test.c-o test.asm
LCFI0:
call _getchar
cmpb $89, %al
je L10
leaq lC1(%rip), %rdi
movsbl %al, %esi
xorl %eax, %eax
call _printf
L7:
xorl %eax, %eax
addq $8, %rsp
LCFI1:
ret
L10:
LCFI2:
leaq lC0(%rip), %rdi
movl $89, %esi
xorl %eax, %eax
call _printf
movl $89, %esi
xorl %eax, %eax
leaq lC1(%rip), %rdi
call _printf
movl $89, %esi
xorl %eax, %eax
leaq lC2(%rip), %rdi
call _printf
jmp L7
现在只有一个cmpb$89,%al
,但也有多个movl$89,%esi
。lC0、lC1和lC2是响应这三个函数的字符串“func1:%c\n”
,“func2:%c\n”
和“func3:%c\n”
作为比较,下面是clang的功能
Ltmp12:
.cfi_offset %rbx, -24
callq _getchar
movsbl %al, %ebx
movzbl %bl, %eax
cmpl $89, %eax
jne LBB3_2
## BB#1:
leaq L_.str(%rip), %rdi
xorl %eax, %eax
movl %ebx, %esi
callq _printf
leaq L_.str.1(%rip), %rdi
xorl %eax, %eax
movl %ebx, %esi
callq _printf
leaq L_.str.2(%rip), %rdi
jmp LBB3_3
LBB3_2:
leaq L_.str.1(%rip), %rdi
与gcc一样,现在只有一个比较。与此类似,L_u.str、L_u.str.1和L.str.2分别是func1、func2和func3中的字符串
从本质上讲,两者都将代码更改为这样
if( input == 'Y' ) {
printf("func1: %c\n", input);
printf("func2: %c\n", input);
printf("func3: %c\n", input);
}
else {
printf("func2: %c\n", input);
}
对
您可以通过查看编译器的汇编代码来检查编译器正在做什么。我已经创建了一个小的C程序来使用,确保它具有一些可变性,这样优化器就不会只对所有内容进行常量折叠
#include <stdio.h>
void func1(const char input) {
printf("func1: %c\n", input);
}
void func2(const char input) {
printf("func2: %c\n", input);
}
void func3(const char input) {
printf("func3: %c\n", input);
}
int main() {
char input = (char)getchar();
if(input == 'Y') {
func1(input);
}
func2(input);
if(input == 'Y') {
func3(input);
}
}
请注意,这是没有优化的。您可以在不必阅读大量汇编的情况下找到比较,查找89,它是Y
的ASCII十进制表示形式
LCFI10:
subq $16, %rsp
call _getchar
movb %al, -1(%rbp)
cmpb $89, -1(%rbp)
jne L5
movsbl -1(%rbp), %eax
movl %eax, %edi
call _func1
L5:
movsbl -1(%rbp), %eax
movl %eax, %edi
call _func2
cmpb $89, -1(%rbp)
jne L6
movsbl -1(%rbp), %eax
movl %eax, %edi
call _func3
L6:
movl $0, %eax
leave
这是两个if块的死记硬背的翻译。您可以看到两个cmpb$89,-1(%rbp)
调用,指示正在进行两次比较
现在进行优化:gcc-S-O3 test.c-o test.asm
LCFI0:
call _getchar
cmpb $89, %al
je L10
leaq lC1(%rip), %rdi
movsbl %al, %esi
xorl %eax, %eax
call _printf
L7:
xorl %eax, %eax
addq $8, %rsp
LCFI1:
ret
L10:
LCFI2:
leaq lC0(%rip), %rdi
movl $89, %esi
xorl %eax, %eax
call _printf
movl $89, %esi
xorl %eax, %eax
leaq lC1(%rip), %rdi
call _printf
movl $89, %esi
xorl %eax, %eax
leaq lC2(%rip), %rdi
call _printf
jmp L7
现在只有一个cmpb$89,%al
,但也有多个movl$89,%esi
。lC0、lC1和lC2是响应这三个函数的字符串“func1:%c\n”
,“func2:%c\n”
和“func3:%c\n”
作为比较,下面是clang的功能
Ltmp12:
.cfi_offset %rbx, -24
callq _getchar
movsbl %al, %ebx
movzbl %bl, %eax
cmpl $89, %eax
jne LBB3_2
## BB#1:
leaq L_.str(%rip), %rdi
xorl %eax, %eax
movl %ebx, %esi
callq _printf
leaq L_.str.1(%rip), %rdi
xorl %eax, %eax
movl %ebx, %esi
callq _printf
leaq L_.str.2(%rip), %rdi
jmp LBB3_3
LBB3_2:
leaq L_.str.1(%rip), %rdi
与gcc一样,现在只有一个比较。与此类似,L_u.str、L_u.str.1和L.str.2分别是func1、func2和func3中的字符串
从本质上讲,两者都将代码更改为这样
if( input == 'Y' ) {
printf("func1: %c\n", input);
printf("func2: %c\n", input);
printf("func3: %c\n", input);
}
else {
printf("func2: %c\n", input);
}
你说的“不变”是指“常数”?取决于。func2()
是否修改cond
?gcc是否可以证明它没有?通过查看程序集输出进行验证。(我100%相信它确实优化了这一点。)我假设“不变”是指“常数表达式”。是的,我猜当它处于循环中时它是不变的,你可能会感兴趣,“不变”是指“常数”?视情况而定。func2()
是否修改cond
?gcc是否可以证明它没有?通过查看程序集输出进行验证。(我100%相信它确实优化了这个。)我假设“不变量”是指“常量表达式”。是的,我猜它在