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

vof42yt1  于 2022-11-24  发布在  其他
关注(0)|答案(2)|浏览(111)

我试着从www.example.com上的一个c++的square函数来理解这godbolt.org。很明显,返回值、参数和局部变量对这个函数使用“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
mxg2im7a

mxg2im7a1#

在这种情况下,区分参数和实参是很方便的。简而言之:参数是由调用者给出的 * 值 *,而参数是保存它们的 * 变量 *。
当呼叫square时,呼叫端会根据标准x86-64呼叫惯例,将 argument 放入rdi寄存器。square接着会配置局部变量 parameter,并将参数放入parameter中。这可让参数像任何其他变数一样使用:由于在这种情况下是被调用方为参数分配内存,因此它必须驻留在帧指针之下。
在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
rqmkfv5c

rqmkfv5c2#

GCC允许“leaf”函数,那些不调用其他函数的函数,不需要创建堆栈框架。自由堆栈是一个公平的游戏,可以满足这些fns的愿望。

相关问题