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

问题描述

我试图根据在 Godbolt.org 上的 C++ 中的平方函数来理解这一点。显然,返回、参数和局部变量使用“rbp - 对齐”作为这个函数。 有人可以解释一下这是怎么可能的吗? 那么在这种情况下 rbp + 对齐会做什么?

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