我已经开始学习汇编语言,并立即偶然发现了争论。我在网上看到的每个教程(例如:http://www.delorie.com/djgpp/doc/ug/asm/calling.html)都解释了传递给函数的参数,将它们推送到堆栈。但是,当我开始尝试时,很明显情况并非如此。 对于简单的功能
movl %edi, -4(%rbp)
movl %esi, -8(%rbp)
movl -8(%rbp), %eax
movl -4(%rbp), %edx
leal (%rdx,%rax), %eax
生成asm后(在AMD64上用gcc编译):
{{1}}
很明显,在EDI和ESI寄存器中传递了参数。当我向函数添加更多参数时,我看到使用了更多的寄存器,并且只有在我达到6个参数后,才开始看到实际从堆栈中读取的值。然而谷歌搜索EDI / ESI寄存器并没有解释他们在论证传递中的特殊作用。我在这里缺少什么?
答案 0 :(得分:1)
您似乎期望在 64位程序中使用传统的 32位调用约定。
如果您将应用程序编译并构建为32位应用程序,那么foo()的编译代码应该按预期工作,并将参数压入堆栈。
对于64位应用程序,调用约定(或ABI)是不同的,并且大多数参数都会在您看到时在寄存器中传递。请在此处查看此其他问题:Where is the X86-64 ABI documented?了解详情。
当引入AMD64架构时,创建新ABI的原因有很多,其中一个原因是要充分利用大量的CPU寄存器。在寄存器中传递一些争论而不是将它们推到堆栈上会更有效率。
答案 1 :(得分:0)
与将它们推入堆栈相比,在寄存器中传递参数更快,并且不使用额外的堆栈内存。这是一项优化功能。按照惯例,编译器可以将前几个参数放在寄存器中,具体取决于CPU架构中可用的寄存器数量。 C ++编译器将始终放置" this"调用对象函数时寄存器中的参数。
寄存器与堆栈的使用以及堆栈中参数的顺序是由编写编译器的人员做出的设计决策。如果您没有与任何外部功能连接,您可以自由地制定自己的约定。如果你是,你将需要知道该函数期望找到参数的位置。
每个编译器都会发布其调用约定。这就是" __ cdecl"和" __ stdcall"函数声明的修饰符告诉你。