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

Mwe*_*lom 2 c++ assembly x86-64 calling-convention stack-frame

我试图根据 godbolt.org 上 c++ 中的平方函数来理解这一点。显然,返回、参数和局部变量使用 \xe2\x80\x9crbp -alignment\xe2\x80\x9d 来实现此函数。\n有人可以解释一下这是如何实现的吗?\n在这种情况下 rbp +alignment 会做什么?

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

编译器(x86-64 gcc 11.1)

\n

生成的程序集:

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

use*_*170 6

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

\n

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

\n

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

\n
square(int):                             # @square(int)\n        push    ebp\n        mov     ebp, esp\n        push    eax\n        mov     eax, dword ptr [ebp + 8]\n        mov     dword ptr [ebp - 4], 5\n        mov     eax, dword ptr [ebp + 8]\n        imul    eax, dword ptr [ebp + 8]\n        add     esp, 4\n        pop     ebp\n        ret\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
square(int):                             # @square(int)\n        mov     eax, edi\n        imul    eax, edi\n        ret\n
Run Code Online (Sandbox Code Playgroud)\n