min*_*234 1 c linux assembly x86-64
我不知道我的标题是否合适,因为我的问题是:我知道有时候(当我想使用argv []时)编译器必须在堆栈上为命令行参数安排空间.现在我编写了一个简单的程序,只是为了看看C编译器如何处理简单的C数组(实际上它以与std::arrays 相同的方式处理它们).我正在使用Manjaro Linux 64位.C代码如下所示:
#include <stdio.h>
int main(){
int a[5] = {1,2,3,4,5};
//printf("%d", a[1]);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
程序集生成输出(来自gcc main.c -fno-asynchronous-unwind-tables -o XD.s -S):
.file "main.c"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
movl $1, -32(%rbp)
movl $2, -28(%rbp)
movl $3, -24(%rbp)
movl $4, -20(%rbp)
movl $5, -16(%rbp)
movl $0, %eax
popq %rbp
ret
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20170109"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
现在,当我取消注释printf语句时,代码如下所示:
.file "main.c"
.section .rodata
.LC0:
.string "%d"
.text
.globl main
.type main, @function
main:
pushq %rbp
movq %rsp, %rbp
subq $32, %rsp
movl $1, -32(%rbp)
movl $2, -28(%rbp)
movl $3, -24(%rbp)
movl $4, -20(%rbp)
movl $5, -16(%rbp)
movl -28(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident "GCC: (GNU) 6.3.1 20170109"
.section .note.GNU-stack,"",@progbits
Run Code Online (Sandbox Code Playgroud)
中间部分很明显,只是打电话printf.但是为什么编译器subq $32, %rsp在这里放了一条线呢?为什么它没有出现在第一个例子中,没有printf声明?
这是编译器的优化.它在第一种情况下实现main是叶函数,因此它知道数组在堆栈上是安全的.而在第二种情况下,对printf的调用将覆盖堆栈帧,因此它通过递增%rsp来保护它.
实际上编译器有很多这样的优化.例如,ABI指定在调用之前堆栈必须是16字节对齐,并且以这样的方式创建帧,即%rsp是16字节对齐的.但是,如果函数不调用任何其他函数或不使用任何SSE指令(需要对齐堆栈帧的指令的一个示例),则它会破坏ABI要求.这些只是为了节省每个字节而进行的微优化.