将参数传递给 ASM 函数

Passing arguments to ASM functions

我已经开始学习汇编语言并立即偶然发现参数传递。我在网上看到的每个教程(例如:http://www.delorie.com/djgpp/doc/ug/asm/calling.html)都将传递给函数的参数解释为将它们推入堆栈。然而,当我开始试验时,很明显情况并非如此。 对于简单函数

int foo(int a, int b) {
    return a + b;
}

生成了以下 asm(使用 gcc 在 AMD64 上编译):

    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -8(%rbp), %eax
    movl    -4(%rbp), %edx
    leal    (%rdx,%rax), %eax

很明显参数是在 EDI 和 ESI 寄存器中传递的。当我向函数添加更多参数时,我看到使用了更多的寄存器,只有在我达到 6 个参数后,我才开始看到实际从堆栈读取的值。然而谷歌搜索 EDI/ESI 寄存器没有给我解释它们在参数传递中的特殊作用。我在这里错过了什么?

与将参数压入堆栈相比,在寄存器中传递参数更快,并且不使用额外的堆栈内存。这是一个优化功能。按照惯例,编译器可能会将前几个参数放在寄存器中,具体取决于 CPU 体系结构中可用的寄存器数量。当调用对象函数时,C++ 编译器总是将 "this" 参数放在寄存器中。

寄存器与堆栈的使用,以及参数在堆栈上的顺序是编写编译器的人做出的设计决定。如果您不与任何外部功能交互,您可以自由制定自己的约定。如果是,您将需要知道该函数期望在哪里找到参数。

每个编译器都会发布其调用约定。这就是函数声明中的“__cdecl”和“__stdcall”修饰符告诉你的。

另请参阅 Microsoft calling conventions, and Wikipedia 说明。

您似乎希望在 64 位 程序中使用传统的 32 位 调用约定。

如果您将应用程序编译和构建为 32 位应用程序,则 foo() 的编译代码应该会按预期工作,并将参数压入堆栈。

对于 64 位应用程序,调用约定(或 ABI)不同,如您所见,大多数参数都是在寄存器中传递的。请在此处查看其他问题:Where is the X86-64 ABI documented? 了解更多详情。

引入 AMD64 架构时,创建新 ABI 的原因有很多,其中之一就是要充分利用更多的 CPU 寄存器。在寄存器中传递一些参数而不是将它们压入堆栈更有效。