Linux 为什么x86在堆栈上放置参数?

Linux 为什么x86在堆栈上放置参数?,linux,assembly,x86,mips,Linux,Assembly,X86,Mips,在MIPS中,参数放置在$a0到$a4寄存器中,以便更快地访问。为什么有些x86架构会选择将参数放在堆栈上而不是寄存器中?这样做的好处是什么?简单的答案是,您必须始终遵循所运行平台的ABI。较长的答案是,您错误地假设每个32位x86平台都将专门使用堆栈进行参数传递。事实上,虽然每个平台都将采用一个标准,但有许多方法,其中任何一种都可以使用。fastcall、cdecl等。真正的答案是,它更多地依赖于编译器而不是处理器,尽管我怀疑x86编译器将参数推送到堆栈上如此常见的原因是x86 CPU总是缺少

在MIPS中,参数放置在$a0到$a4寄存器中,以便更快地访问。为什么有些x86架构会选择将参数放在堆栈上而不是寄存器中?这样做的好处是什么?

简单的答案是,您必须始终遵循所运行平台的ABI。较长的答案是,您错误地假设每个32位x86平台都将专门使用堆栈进行参数传递。事实上,虽然每个平台都将采用一个标准,但有许多方法,其中任何一种都可以使用。fastcall、cdecl等。

真正的答案是,它更多地依赖于编译器而不是处理器,尽管我怀疑x86编译器将参数推送到堆栈上如此常见的原因是x86 CPU总是缺少寄存器。当您消除保留寄存器时,剩下三个-EAX、ECX和EDX,它们对应于原始16位x86指令集中的AX、CX和DX。据我所知,Intel系列中第一个提高这一限制的处理器是64位AMD架构,它又增加了8个,编号为R9到R15。五个保留的寄存器获得新名称,但它们仍然保留

为了强化我关于它依赖于编译器的断言,您只需查看Microsoft.NET Framework附带的本机代码生成器,它展示了一种混合方法,其中前两个参数进入ECX或RCX以及EDX或RDX,而其他参数进入堆栈


即使是在微软Visual C++编译器中生成的C/C++代码中,尽管最常见的调用约定是,y-cDECL和y-sdDelk,使用栈,第三,y-FASTLCAL,使用寄存器。此外,我还看到了使用这两种方法的汇编代码;如果它需要与C对话,则例程在寄存器中预期参数,但仅从库中的其他例程接收调用的私有例程使用寄存器。

寄存器自然更快,速度要快得多,但必须拥有足够的寄存器。传统上,X86寄存器很少,所以基于堆栈的方法是要走的路,在历史上,它通常是走的路,RISC和其他的伴随着更多的寄存器和使用寄存器的想法为最初的几个参数,也许返回值现在是要考虑的事情。x86现在有更多的寄存器,但通常是基于堆栈的,尽管我认为zortech或watcom,甚至gcc现在有一个命令行选项来使用一些寄存器,但必须通过研究来确认或否认这一点。但从历史上看,它使用堆栈作为参数和寄存器

ARM、MIPS等都有有限数量的寄存器,因此最终会转储到堆栈中,如果您保留/控制参数的数量和大小,并且有时进行排序,则可以尝试限制这一数量并提高性能

归根结底,底线是某个人或某个团队定义了调用约定,这最终是编译器作者的选择,不管芯片/处理器设计师是否有建议,编译器定义了它的调用约定是什么,是遵循建议还是做自己的事。没有理由创建主要基于堆栈的MIPS或ARM编译器/工具链指令集本身可能会指定基于堆栈或基于寄存器的返回,也可能是可选的,同样,我们非常欢迎您使用以寄存器开始的约定来创建x86编译器,然后在使用了一定数量的寄存器后将其移动到堆栈中


所以有一点历史和一点因为他们选择…

微软使用ECX和EDX作为前两个参数。使用ECX、EDX、R8和R9作为前四个参数和/或前四个xmm寄存器作为浮点值。由于我很少使用它,我已经忘记了有关fastcall约定的细节。本质上,NGEN使用的是fastcall。你问题的前提是致命的缺陷。没有基于处理器的参数必须在堆栈上的要求,也没有对使用寄存器传递参数的任何限制。看一看原始的IBMPCBIOS:BIOS有在寄存器中传递参数的例程。