旧的和新的GCC生成的汇编代码的for循环的差异

kha*_*vah 3 c assembly gcc

我正在阅读有关汇编代码的章节,其中有一个例子.这是C程序:

int main()
{
    int i;
    for(i=0; i < 10; i++)
    {
        puts("Hello, world!\n");
    }
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这是书中提供的汇编代码:

0x08048384 <main+0>:    push ebp
0x08048385 <main+1>:    mov ebp,esp
0x08048387 <main+3>:    sub esp,0x8
0x0804838a <main+6>:    and esp,0xfffffff0
0x0804838d <main+9>:    mov eax,0x0
0x08048392 <main+14>:   sub esp,eax
0x08048394 <main+16>:   mov DWORD PTR [ebp-4],0x0
0x0804839b <main+23>:   cmp DWORD PTR [ebp-4],0x9
0x0804839f <main+27>:   jle 0x80483a3 <main+31>
0x080483a1 <main+29>:   jmp 0x80483b6 <main+50>
0x080483a3 <main+31>:   mov DWORD PTR [esp],0x80484d4
0x080483aa <main+38>:   call 0x80482a8 <_init+56>
0x080483af <main+43>:   lea eax,[ebp-4]
0x080483b2 <main+46>:   inc DWORD PTR [eax]
0x080483b4 <main+48>:   jmp 0x804839b <main+23>
Run Code Online (Sandbox Code Playgroud)

这是我的版本的一部分:

   0x0000000000400538 <+8>: mov    DWORD PTR [rbp-0x4],0x0
=> 0x000000000040053f <+15>:    jmp    0x40054f <main+31>
   0x0000000000400541 <+17>:    mov    edi,0x4005f0
   0x0000000000400546 <+22>:    call   0x400410 <puts@plt>
   0x000000000040054b <+27>:    add    DWORD PTR [rbp-0x4],0x1
   0x000000000040054f <+31>:    cmp    DWORD PTR [rbp-0x4],0x9
   0x0000000000400553 <+35>:    jle    0x400541 <main+17>
Run Code Online (Sandbox Code Playgroud)

我的问题是,为什么在本书的版本中它将0分配给变量(mov DWORD PTR [ebp-4],0x0)并在之后进行比较cmp但是在我的版本中,它分配然后它确实jmp 0x40054f <main+31>在哪里cmp

分配和比较似乎更合理,没有任何jump,因为它就像在for循环中.

Art*_*Art 5

为什么你的编译器做的不同于本书中使用的不同编译器?因为它是一个不同的编译器.没有两个编译器会编译所有相同的代码,即使是非常简单的代码也可以被两个不同的编译器甚至同一个编译器的两个版本编译得大不相同.很明显,两者都是在没有任何优化的情况下编译的,通过优化,结果会更加不同.

让我们来看看for循环的作用.

for (i = 0; i < 10; i++) {
    code;
}
Run Code Online (Sandbox Code Playgroud)

让我们把它写得更接近第一个编译器生成的汇编程序.

        i = 0;
start:  if (i > 9) goto out;
        code;
        i++;
        goto start;
out:
Run Code Online (Sandbox Code Playgroud)

现在"我的版本"也是一样的:

        i = 0;
        goto cmp;
start:  code;
        i++;
cmp:    if (i < 10) goto start;
Run Code Online (Sandbox Code Playgroud)

这里明显不同的是,在"我的版本"中,只有一个跳转在循环中执行,而书籍版本有两个.由于CPU对分支的敏感程度,在更现代的编译器中生成循环是一种非常常见的方法.许多编译器即使没有任何优化也会生成这样的代码,因为它在大多数情况下表现更好.较旧的编译器没有这样做,因为要么他们没有考虑它,要么这个技巧是在编译本书中的代码时未启用的优化阶段执行的.

请注意,启用了任何类型的优化的编译器甚至不会首先执行此操作,goto cmp因为它会知道它是不必要的.尝试编译你的代码并启用优化(你说你使用gcc,给它-O2标志),看看它后面会有多大的不同.