为什么编译器不使用“sub*”和“add*”在堆栈上分配和释放局部变量?

Ghj*_*hdf 2 c compiler-construction macos assembly llvm-gcc

根据一些教科书,编译器将使用sub*为局部变量分配内存。

比如我写了一个Hello World程序:

int main()
{
    puts("hello world");
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我想这将在 64 位操作系统上编译为一些汇编代码:

    subq    $8, %rsp
    movq    $.LC0, (%rsp)
    calq    puts
    addq    $8, %rsp
Run Code Online (Sandbox Code Playgroud)

所述subq分配8字节存储器(一个点的大小),用于该参数和addq解除分配它。

但是当我输入时gcc -S hello.c(我在 Mac OS X 10.8 上使用 llvm-gcc),我得到了一些汇编代码。

.section    __TEXT,__text,regular,pure_instructions
.globl  _main
.align  4, 0x90
_main:
Leh_func_begin1:
       pushq    %rbp
Ltmp0:
    movq    %rsp, %rbp
Ltmp1:
    subq    $16, %rsp
Ltmp2:
    xorb    %al, %al
    leaq    L_.str(%rip), %rcx
    movq    %rcx, %rdi
    callq   _puts
    movl    $0, -8(%rbp)
    movl    -8(%rbp), %eax
    movl    %eax, -4(%rbp)
    movl    -4(%rbp), %eax
    addq    $16, %rsp
    popq    %rbp
    ret

   .......

L_.str:
      .asciz     "hello world!"
Run Code Online (Sandbox Code Playgroud)

在此周围callq没有任何addqsubq。为什么?的功能是addq $16, %rsp什么?

感谢您提供任何意见。

Ale*_*nze 5

您的main(). 您可能拥有的只是一个伪变量,用于传递给字符串puts()地址的参数"hello world"

根据您上次的反汇编,调用约定似乎是第一个参数 toputs()rdi寄存器中而不是在堆栈中传递,这就是没有为此参数分配任何堆栈空间的原因。

但是,由于您在禁用优化的情况下编译程序,您可能会遇到一些不必要的堆栈空间分配以及对该空间的读取和写入。

这段代码说明了这一点:

subq    $16, %rsp ; allocate some space
...
movl    $0, -8(%rbp) ; write to it
movl    -8(%rbp), %eax ; read back from it
movl    %eax, -4(%rbp) ; write to it
movl    -4(%rbp), %eax ; read back from it
addq    $16, %rsp
Run Code Online (Sandbox Code Playgroud)

这四个mov指令相当于一个 simple movl $0, %eax,不需要内存来做到这一点。

如果您-O2在编译命令中添加优化开关,您将在反汇编中看到更有意义的代码。

另请注意,某些空间分配可能仅用于保持堆栈指针对齐的目的,这可以提高性能或避免未对齐的内存访问问题(如果启用,您可能会在未对齐的访问中获得 #AC 异常)。

上面的代码也显示了它。看,这 4mov条指令只使用 8 字节的内存,而addsub指令将堆栈扩大和缩小 16字节。