为什么参数分配在帧指针下方而不是上方?

Why are parameters allocated below the frame pointer instead of above?

我试图根据 godbolt.org 中的 c++ 中的平方函数来理解这一点。显然,return、参数和局部变量使用“rbp - alignment”来实现这个功能。 有人可以解释这是怎么可能的吗? 那么在这种情况下 rbp + alignment 会做什么?

int square(int num){
    int n = 5;// just to test how locals are treated with frame pointer
    return num * num;
}

编译器 (x86-64 gcc 11.1)

生成的程序集:

square(int):
    push rbp
    mov rbp, rsp 
    mov DWORD PTR [rbp-20], edi. ;\Both param and local var use rbp-*
    mov DWORD PTR[rbp-4], 5.     ;//
    mov eax, DWORD PTR [rbp-20]
    imul eax, eax
    pop rbp
    ret

这是可以方便区分参数和自变量的情况之一。简而言之:参数是调用者给出的,而参数是保存它们的变量

当调用 square 时,调用者根据标准 x86-64 调用约定将 参数 放入 rdi 寄存器中。 square 然后分配一个局部变量,即 参数 ,并将参数放在参数中。这允许参数像任何其他变量一样使用:读取、写入、获取其地址等。由于在这种情况下是为参数分配内存的被调用方,因此它必须驻留在帧指针下方。

使用在堆栈上传递参数的 ABI,被调用方将能够重用包含参数的堆栈槽作为参数。这正是 x86-32 上发生的事情(通过 -m32 看你自己):

square(int):                             # @square(int)
        push    ebp
        mov     ebp, esp
        push    eax
        mov     eax, dword ptr [ebp + 8]
        mov     dword ptr [ebp - 4], 5
        mov     eax, dword ptr [ebp + 8]
        imul    eax, dword ptr [ebp + 8]
        add     esp, 4
        pop     ebp
        ret

当然,如果启用了优化,编译器就不会费心在被调用者的堆栈上分配参数;它会直接使用寄存器中的值:

square(int):                             # @square(int)
        mov     eax, edi
        imul    eax, edi
        ret