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

Why x86 places arguments on stack?

在 MIPS 中,参数被放置在 $a0 到 $a4 寄存器中以加快访问速度。为什么一些 x86 架构设计选择将参数放在堆栈而不是寄存器中?这样做有什么好处?

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

真正的答案是它更多地依赖于编译器而不是处理器,尽管我怀疑 x86 编译器将参数压入堆栈如此普遍的原因是 x86 CPU 一直遭受寄存器短缺的困扰。当你删除保留寄存器时,你只剩下三个 - EAX、ECX 和 EDX,它们对应于原始 16 位 x86 指令集中的 AX、CX 和 DX。据我所知,英特尔系列中第一个提高该限制的处理器是 64 位 "AMD" 架构,它又增加了八个,编号为 R9 到 R15。五个保留寄存器获得了新名称,但它们仍然保留。

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

即使在 Microsoft Visual C++ 编译器生成的 C/C++ 代码中,尽管最常见的调用约定 __cdecl 和 __stdcall 使用堆栈,第三个,__fastcall,使用寄存器。此外,我还看到了同时使用两者的汇编代码;如果它需要与 C 对话,例程需要寄存器中的参数,但仅从库中的其他例程接收调用的私有例程使用寄存器。

寄存器自然更快,速度相当快,但您必须拥有足够多的寄存器。 x86 传统上只有很少的寄存器,所以基于堆栈的方法是要走的路,在那个时候在历史上它通常是要走的路,risc 和其他人一起出现了更多的寄存器和第一个使用寄存器的想法现在需要考虑的参数很少,也许 return 值。 x86 现在有更多的寄存器,但通常是基于堆栈的,尽管我认为 zortech 或 watcom 甚至 gcc 现在有一个命令行选项来使用一些寄存器,必须通过研究来确认或否认这一点。但从历史上看,它曾将堆栈用于参数和寄存器。

ARM、MIPS 等都有有限数量的寄存器,因此最终会转储到堆栈中,如果您 keep/control 您的参数数量和大小以及有时排序,您可以尝试限制它并提高性能。

归根结底是某人或某个团队定义了调用约定,这是编译器作者的最终选择,chip/processor 设计者是否推荐编译器定义并不重要它的调用约定是遵循建议还是做自己的事情。没有理由创建一个主要基于堆栈的 MIPS 或 ARM compiler/toolchain(指令集本身可能决定基于堆栈或寄存器的 returns,或者它可能是可选的),同样你不仅仅是欢迎制作一个 x86 编译器,其约定从寄存器开始,然后在使用了一定数量的寄存器后移至堆栈。

所以有一点历史,还有一点是因为他们选择...