我一直在研究一些简单的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++;
    }


}

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

-Os编译的/> -O2编译

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>


#1 楼

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

-Os标志进行了优化以减小代码大小。使用-Os生成的代码使用dec ebx而不是sub ebx, 0x1,因为dec ebx是2字节指令,而sub ebx, 0x1是3字节指令(请注意下一条指令开始的地址)。这就说明了微小的差异。

评论


这确实解释了一个方向的差异,但没有解释另一方向的差异。看来sub一定比sub有优势,否则O2不会选择它。

– PlasmaHH
2014年3月11日13:03

通过一些研究,我认为我找到了O2使用sub而不是dec的原因。英特尔®64和IA-32架构优化参考手册在附录C的表C-19中,添加/订阅的延迟较低。

– pyCtrl_
2014年3月13日在12:37

#2 楼

因为我无法发表评论,所以我将尝试修复pnak4j的答案中的一些错误。 。在以下情况下,dec ebx根据(DEC)的结果相应地设置ZF标志:零或非零。然后,如果不为零,则ebx-1会执行跳转(JNE / JNE相同)。 JNZ不是条件跳转,因此在JMP / CMP之后没有太大意义。

评论


jmp是指有条件的跳转。比较后无条件跳转没有多大意义。已被编辑。谢谢。

– pank4j
2014年3月11日在7:20

dec ebx是0xff 0xcb

– PlasmaHH
2014年3月11日13:07