最近想了解一下c是如何编译为汇编语言的
首先写了一个相当简单的c
1 |
|
使用gcc -S
编译为汇编语言:
1 | "hello.c" |
以”点”做为前缀的指令都是用来指导汇编器的命令。
精简一下差不多这样:
1 | .LC0: |
首先了解一下几个寄存器[1]
- %rbp 是栈帧指针,用于标识当前栈帧的起始位置
- %rsp 是堆栈指针寄存器,通常会指向栈顶位置,堆栈的 pop 和push 操作就是通过改变 %rsp 的值即移动堆栈指针的位置来实现的。
- %eax 是”累加器”(accumulator), 它是很多加法乘法指令的缺省寄存器。
- %edx 则总是被用来放整数除法产生的余数。
- %rip 指令地址寄存器,用来存储 CPU 即将要执行的指令地址。每次 CPU 执行完相应的汇编指令之后,rip 寄存器的值就会自行累加;rip 无法直接赋值,call, ret, jmp 等指令可以修改 rip。
- %rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。
- ESI/EDI 分别叫做”源/目标索引寄存器”(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
1 | pushq %rbp |
movl $1, -16(%rbp)
把1移入寄存器rbq中-16是地址起始位置movl $2, -12(%rbp)
跟上面相同movl -16(%rbp), %edx
把-16(%rbp)
移入寄存器edx中,上面可知就是把1移入edx中movl -12(%rbp), %eax
跟上面类似 不过这次用的是寄存器eaxaddl %edx, %eax
把edx和eax中的内容相加,并存入edx中movl %eax, -8(%rbp)
把eax中的内容存入rbq中位置为-8movl $7, -4(%rbp)
把7存入rbq位置为-4,对应c代码为int d = 3 + 4
,由此可知编译器在编译时其实是会进行相应优化的.LC0(%rip)
は、printfが使うアドレス、前もってprintfで使用する書式を設定している(就是说设置printf使用的地址,并提前设置格式)[2]call printf@PLT
调用printf
leave
离开printfret
:结束当前函数
接下来我们可以简单的实战一下:
给gcc开启O3级别的优化:
1 | gcc -O3 -S |
生成的汇编码:
1 | "hello.c" |
从以上汇编代码中我们可以发现,优化之后的汇编代码将最后相加的值给了内存,而不像之前那样先占用两个内存空间然后再相加.
[1] x86-64 下函数调用及栈帧原理
[2] アセンブラ学習log_1