“sub $16, %rsp”指令的用途是什么?

car*_*ass 4 assembly gcc x86-64 calling-convention

让我们采用以下基本 C 函数及其生成的故意未优化的程序集:

int main() {
    int i = 4;
    return 3;
}
Run Code Online (Sandbox Code Playgroud)

它产生以下(未优化的)程序集,这对我来说都是有意义的:

main:
        pushq   %rbp
        movq    %rsp, %rbp
        movl    $4, -4(%rbp)
        movl    $3, %eax
        popq    %rbp
        ret
Run Code Online (Sandbox Code Playgroud)

然而,当我添加一个函数调用时,有两条指令我不太明白:

void call() {};
int main() {
    int i = 4;
    call();
    return 3;
}
Run Code Online (Sandbox Code Playgroud)
main:
        pushq   %rbp
        movq    %rsp, %rbp
        subq    $16, %rsp     <-- why is it subtracting 16?
        movl    $4, -4(%rbp)
        movl    $0, %eax      <-- why is it first clearing the %eax register?
        call    call
        movl    $3, %eax
        leave
        ret
Run Code Online (Sandbox Code Playgroud)

如果堆栈帧需要 16 字节对齐,有什么帮助吗subq $16, %rsp?最初的指令不是pushq %rbp已经将其偏移了 8 而现在是 +24 吗?或者说上面这两行的要点是什么?

Flo*_*mer 5

第一个变体将局部变量存储在红色区域中,这是堆栈指针下方的 128 字节区域,信号处理程序不会更改该区域。第二个变体不能使用红色区域,因为callq指令写入(原始)红色区域,破坏了存储在那里的局部变量。(当然,被调用的函数也可以写入原始的红色区域。)

%eax设置为零,因为函数定义没有声明原型,因此编译器必须假设它是一个可变参数函数。(%eax实际上%al)用于优化可变参数函数的实现

  • 事实上:C 不是 C++,“void call()”也不是带有*无*参数的函数声明,它是带有*未指定*参数的函数声明。OP:你想写“void call(void)”,然后你会看到该指令消失了。 (2认同)