gcc-if语句优化

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)出现

gcc是否优化代码,例如:

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%相信它确实优化了这个。)我假设“不变量”是指“常量表达式”。是的,我猜它在