我试图了解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 $10, -12(%rbp)
000b C745F80A movl $10, -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等?
答案 0 :(得分:4)
推rbp? - 这是堆栈框架还是什么?
是。编译器为局部变量创建堆栈帧。 push %rbp
/ movq %rsp, %rbp
是执行此操作的标准方法。它允许轻松访问局部变量。
将10移入堆栈?抵消-12?为什么12,为什么它是否定的?
在这种情况下,编译器选择使用从int
到-12(%rbp)
的堆栈的4字节(-9(%rbp)
大小)部分作为变量x
。< / p>
创建堆栈帧后,您可以访问具有负偏移的局部变量,以及具有正偏移的函数参数:
------------------------------------------------------
| 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也占用空间以及函数的返回地址,因此需要在任何参数偏移量中添加16个字节。 (32位系统为8个字节)
通常,在使用局部变量进行任何工作之前,您必须更新RSP
,例如:subq $12, %rsp
。离开该功能时,请使用addq $12, %rsp
或leave
。此示例更新堆栈指针以显示我们在堆栈上使用12个字节。完成它们后,只需恢复堆栈指针即可。但是,在您的示例中,不需要这样做,因为该函数对堆栈没有其他用途,而不是局部变量。
将10移动到堆栈中,但这次是-8而不是-12
再次引用一个局部变量,除了这次,编译器选择了从-8(%rbp)
到-5(%rbp)
的4字节部分,用于变量y
。
在这种情况下,pop %rbp
将函数末尾的堆栈恢复为条目之前的堆栈:
------------------------------------------------------
| R |
New stack (locals) | B | Old stack (parameters)
| P |
------------------------------------------------------
^
RSP points here, so a `pop %rbp` will restore both RSP and RBP
编译器可能首先尝试使用EAX
和EDX
,因为EAX
是为数学运算而设计的,EDX
是为通用数据运算而设计的。你会经常发现它们在操作中配对。
答案 1 :(得分:0)
要了解编译器生成的程序集,您必须了解堆栈帧。 SP是堆栈指针,BP指向当前堆栈帧,用于寻址局部变量(因此将值&#34; 10&#34;移动到[bp-12]和[bp-8]。然后加载这是添加的第一个可用寄存器(在这种情况下是ax和dx)并执行添加。最后,它恢复旧堆栈并返回。