2015-10-23 104 views
1

我想了解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 

但是我有一些很难理解尤其是在这个片段是怎么回事。我了解所有的标签和一些装配。这是我认为它的作用:

  • push rbp? - 这是一个堆栈框架还是什么?
  • 设置堆栈指针指向基址指针? (即清除堆栈)
  • 将10移入堆栈?偏移量为-12?为什么12,为什么是负面的?
  • 在-8代替-12在-8(4差异,也许字节或什么?)
  • 移动值在-12移动10插入栈,虽然此时进入EAX
  • 移动值到EDX
  • add eax and edc
  • 将值从eax移入堆栈
  • pop rbp?函数堆栈帧结束可能吗?
  • 从函数返回??

任何人都可以澄清这个组件的某些点,也许是编译器具有推理在选择-8,-12,为什么选择EAX和EDC在其他一些寄存器,它为什么push和pop RBP等?

+2

对于你的每一个问题的答案是“阅读关于堆栈帧的好介绍文本” –

+0

@ WumpusQ.Wumbley哈哈,好吧我会这么做的! – mosmo

回答

3

push rbp? - 这是一个堆栈框架还是什么?

是。编译器为局部变量创建一个堆栈帧。 push %rbp/movq %rsp, %rbp是这样做的标准方法。它允许轻松访问局部变量。

将10移入堆栈?偏移量为-12?为什么12,为什么是负面的?

在这种情况下,编译器选择使用的堆叠的4个字节(int尺寸)部分从-12(%rbp)-9(%rbp)至用于可变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也占用空间,以及返回该函数的地址,您需要将16个字节添加到任何参数偏移量。 (对于32位系统,8字节)

通常,在使用本地变量进行任何工作之前,必须更新RSP,如下所示:subq $12, %rsp。离开功能时,请使用addq $12, %rspleave。本示例更新堆栈指针以显示我们在堆栈上使用了12个字节。当你完成它们时,你只需恢复堆栈指针。在你的例子中,虽然没有这个需要,因为这个函数没有其他地方变量用于堆栈。

在-8代替-12

一旦再次移动10插入堆,尽管此时,引用一个局部变量,但此时,编译器选择了4字节部分开始在-8(%rbp)-5(%rbp)变量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 

编译器可能会尝试使用EAXEDX首先是因为EAX是专为数学操作和EDX专为通用数据操作而设计。你经常会发现他们在操作中配对。

+1

编译器实际上不应该在它为本地使用EBP负偏移之前实际更新SP?还是仅仅因为该方法不调用任何子函数而优化掉了它? – PMF

+0

辉煌,谢谢! – mosmo

0

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