GCC 循环优化

逆向工程 拆卸
2021-06-23 20:51:20

我一直在查看一些简单的 C 代码以及使用不同优化级别的 GCC 的不同输出。

C代码

#include <stdio.h>

int main() {
    int i = 0;

    while(i<10) {
            printf("Hello\n");
            i++;
    }

    i = 0;

    while(i<10) {
            printf("i: %d\n", i);
            i++;
    }

}

当我使用-Osor编译代码时-O2,第一个循环的工作方式略有不同。它是减少而不是增加,它有两种不同的方式。我想知道为什么它递减的,而不是在代码中增加等,之间的差异小-Os-O2

-Os 编译

0x400486 <main+6>       mov     edi,0x40068c
0x40048b <main+11>      call    0x400450 <puts@plt>
0x400490 <main+16>      dec     ebx
0x400492 <main+18>      jne     0x400486 <main+6>

-O2 编译

0x400490 <main+16>      mov    edi,0x40069c
0x400495 <main+21>      call   0x400450 <puts@plt>
0x40049a <main+26>      sub    ebx,0x1
0x40049d <main+29>      jne    0x400490 <main+16> 
2个回答

通过递减,编译器可以利用jne(如果不等于/零则跳转)进行比较(到零)并跳转到单个指令中。在递增的情况下,它必须执行一个cmp/test(用 10)然后像jnz/jne. 我相信这是优化的一部分。

-Os标志优化以降低代码大小。使用生成的代码-Os的用途dec ebx,而不是sub ebx, 0x1,由于dec ebx是2个字节的指令,而sub ebx, 0x1是一个3字节的指令(注意的下一个指令开始的地址)。这解释了微小的差异。

因为我无法发表评论,所以我将尝试修复 pnak4j 回答中的一些不准确之处。

dec ebx确实是 1 字节指令(我不知道为什么它看起来是 2 字节)。DEC在以下情况下ZF根据 ( ebx-1)的结果相应地设置标志:零或非零。然后,JNE如果不是零则跳转(JNE/JNZ是相同的)。JMP不是条件跳转,因此在CMP/之后没有多大意义TEST