编译器在这段汇编代码中做了什么?

What does the compiler do in this assembly code?

我试图了解 C 编译器在编译为汇编时会做什么。我编译成汇编的代码是这样的:

void main() {
    int x = 10;
    int y = 10;
    int a = x + y;
}

生成以下程序集:

                .Ltext0:
                    .globl  main
                main:
                .LFB0:
0000 55             pushq   %rbp
0001 4889E5         movq    %rsp, %rbp
0004 C745F40A       movl    , -12(%rbp)
000b C745F80A       movl    , -8(%rbp)
0012 8B45F8         movl    -8(%rbp), %eax
0015 8B55F4         movl    -12(%rbp), %edx
0018 01D0           addl    %edx, %eax
001a 8945FC         movl    %eax, -4(%rbp)
001d 5D             popq    %rbp
001e C3             ret

但是,我在理解这段代码中具体发生的事情时遇到了一些困难。我了解所有标签和一些组件。这是我认为它的作用:

谁能澄清这个程序集的某些要点,也许是编译器选择 -8、-12 的原因,为什么它选择 eax 和 edc 而不是其他一些寄存器,为什么它压入和弹出 rbp,等等?

push rbp? - is this for a stack frame or something?

是的。编译器为局部变量创建一个栈帧。 push %rbp / movq %rsp, %rbp 是执行此操作的标准方法。它允许轻松访问局部变量。

moves 10 into stack? Offset by -12? Why 12, and why is it negative?

在这种情况下,编译器选择使用堆栈中从 -12(%rbp)-9(%rbp) 的 4 字节(int 大小)部分作为变量 x .

创建堆栈帧后,您可以访问具有负偏移量的局部变量和具有正偏移量的函数参数:

------------------------------------------------------
                        | R |
     New stack (locals) | B | Old stack (parameters)
                        | P |
------------------------------------------------------
                          ^
                          RBP is updated to point here as well so you get negative offsets (to the left) for locals and positive offsets (to the right) for parameters.

注意,由于存储的RBP也占用了space,以及函数的return地址,所以任何一个参数偏移量都需要增加16个字节。 (32 位系统为 8 个字节)

通常,您必须先更新 RSP,然后才能使用局部变量,例如:subq , %rsp。离开函数时,使用addq , %rspleave。此示例更新堆栈指针以显示我们在堆栈上使用了 12 个字节。完成它们后,只需恢复堆栈指针即可。但是,在您的示例中,none 是必需的,因为该函数除了局部变量外对堆栈没有其他用途。

moves 10 into stack, though this time at -8 instead of -12

再次引用局部变量,除了这一次,编译器为变量 y.

选择了从 -8(%rbp)-5(%rbp) 的 4 字节部分

在这种情况下,pop %rbp 将函数末尾的堆栈恢复为进入前的状态:

------------------------------------------------------
                        | R |
     New stack (locals) | B | Old stack (parameters)
                        | P |
------------------------------------------------------
                          ^
                          RSP points here, so a `pop %rbp` will restore both RSP and RBP

编译器可能首先尝试使用 EAXEDX,因为 EAX 是为数学运算而设计的,而 EDX 是为一般数据运算而设计的。您会经常发现它们在操作中配对。

要了解编译器生成的程序集,您必须了解堆栈帧。 SP 是堆栈指针,BP 指向当前堆栈帧,用于寻址局部变量(因此将值“10”移动到 [bp-12] 和 [bp-8]。然后它将其加载到第一个可用的注册加法(本例中为 ax 和 dx)并执行加法。最后,它恢复旧堆栈和 returns。