为什么海湾合作委员会使用其他寄存器将值推向堆栈?

发布于 2025-02-09 05:28:58 字数 1497 浏览 1 评论 0原文

该C代码

void test_function(int a, int b, int c, int d) {}

int main() {
  test_function(1, 2, 3, 4);
  return 0;
}

由GCC(无标志,版本12.1.1,Target x86_64-REDHAT-LINUX)编译为

0000000000401106 <test_function>:
  401106:   55                      push   rbp
  401107:   48 89 e5                mov    rbp,rsp
  40110a:   89 7d fc                mov    DWORD PTR [rbp-0x4],edi
  40110d:   89 75 f8                mov    DWORD PTR [rbp-0x8],esi
  401110:   89 55 f4                mov    DWORD PTR [rbp-0xc],edx
  401113:   89 4d f0                mov    DWORD PTR [rbp-0x10],ecx
  401116:   90                      nop
  401117:   5d                      pop    rbp
  401118:   c3                      ret    

0000000000401119 <main>:
  401119:   55                      push   rbp
  40111a:   48 89 e5                mov    rbp,rsp
  40111d:   b9 04 00 00 00          mov    ecx,0x4
  401122:   ba 03 00 00 00          mov    edx,0x3
  401127:   be 02 00 00 00          mov    esi,0x2
  40112c:   bf 01 00 00 00          mov    edi,0x1
  401131:   e8 d0 ff ff ff          call   401106 <test_function>
  401136:   b8 00 00 00 00          mov    eax,0x0
  40113b:   5d                      pop    rbp
  40113c:   c3                      ret  

为什么有其他寄存器(ecxedx edx < /code>,esiedi)用作中间存储,用于值1、2、3、4,而不是直接将它们放入rbp中?

This C code

void test_function(int a, int b, int c, int d) {}

int main() {
  test_function(1, 2, 3, 4);
  return 0;
}

gets compiled by GCC (no flags, version 12.1.1, target x86_64-redhat-linux) into

0000000000401106 <test_function>:
  401106:   55                      push   rbp
  401107:   48 89 e5                mov    rbp,rsp
  40110a:   89 7d fc                mov    DWORD PTR [rbp-0x4],edi
  40110d:   89 75 f8                mov    DWORD PTR [rbp-0x8],esi
  401110:   89 55 f4                mov    DWORD PTR [rbp-0xc],edx
  401113:   89 4d f0                mov    DWORD PTR [rbp-0x10],ecx
  401116:   90                      nop
  401117:   5d                      pop    rbp
  401118:   c3                      ret    

0000000000401119 <main>:
  401119:   55                      push   rbp
  40111a:   48 89 e5                mov    rbp,rsp
  40111d:   b9 04 00 00 00          mov    ecx,0x4
  401122:   ba 03 00 00 00          mov    edx,0x3
  401127:   be 02 00 00 00          mov    esi,0x2
  40112c:   bf 01 00 00 00          mov    edi,0x1
  401131:   e8 d0 ff ff ff          call   401106 <test_function>
  401136:   b8 00 00 00 00          mov    eax,0x0
  40113b:   5d                      pop    rbp
  40113c:   c3                      ret  

Why are additional registers (ecx, edx, esi, edi) used as intermediary storage for values 1, 2, 3, 4 instead of putting them into rbp directly?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

醉梦枕江山 2025-02-16 05:28:59

作为中间存储”:您的混乱似乎是这一部分。

ABI指定这些函数参数是在您看到的寄存器中传递的(请参阅问题下的评论)。寄存器不仅用作中介。该值根本不应该放在堆栈上。除非功能需要将寄存器重复使用其他内容或将指针传递给功能参数或类似的东西,否则它们一直留在寄存器中。

您在test_function中看到的只是不启用优化的构造的工件。将寄存器放在堆栈上的MOV指令毫无意义,因为之后什么都没做。堆栈指针立即恢复,然后函数返回。

整个函数应该只是一个ret指令。参见 https://godbolt.org/z/qg9gjmohy使用。

如果没有优化,则能够启用编译器,即使指令毫无意义,也不会尝试删除指令,并且它总是将变量值存储到内存中,并再次从内存中加载它们,即使它们可以保存在寄存器中。这就是为什么查看-O0汇编几乎总是毫无意义的。

"as intermediary storage": You confusion seems to be this part.

The ABI specifies that these function arguments are passed in the registers you are seeing (see comments under the question). The registers are not just used as intermediary. The value are never supposed to be put on the stack at all. They stay in the register the whole time, unless the function needs to reuse the register for something else or pass on a pointer to the function parameter or something similar.

What you are seeing in test_function is just an artifact of not compiling with optimizations enabled. The mov instructions putting the registers on the stack are pointless, since nothing is done with them afterwards. The stack pointer is just immediately restored and then the function returns.

The whole function should just be a single ret instruction. See https://godbolt.org/z/qG9GjMohY where -O2 is used.

Without optimizations enabled the compiler makes no attempt to remove instructions even if they are pointless and it always stores values of variables to memory and loads them from memory again, even if they could have been held in registers. That's why it is almost always pointless to look at -O0 assembly.

街角卖回忆 2025-02-16 05:28:59

寄存器用于参数调用该函数。标准调用转换呼叫要求将aguments放置在某些寄存器中,因此您在MAIN中看到的代码将参数放入这些寄存器中,而test_function中的代码期望它们在这些寄存器中,并从那里读取它们。

因此,您的后续问题可能是“为什么test_function将这些参数复制到堆栈中?”。这是因为您在没有优化的情况下进行编译,因此编译器会为每个参数和本地VAR产生效率低下的代码,堆栈框架中的分配空间,并将其输入寄存器从其输入寄存器复制到堆栈框架中,作为功能Prolog的一部分。如果要在函数中使用这些值,即使它们可能仍在寄存器中,也会从堆栈框架位置读取它们。如果您使用-O编译,则会看到编译器摆脱所有这些,因为不需要堆栈框架。

The registers are used for the arguments to call the function. The standard calling convertion calls for aguments to be placed in certain register, so the code you see in main puts the arguments into those registers and the code in test_function expects them in those registers and reads them from there.

So your follow-on question might be "why is test_function copying those argument on to the stack?". That's because you're compiling without optimization, so the compiler produces inefficient code, allocation space in the stack frame for every argument and local var and copying the arguments from their input register into the stack frame as part of the function prolog. If you were to use those values in th function, you would see it reading them from the stack frame locations even though they are probably still in the registers. If you compile with -O, you'll see the compiler get rid of all this, as the stack frame is not needed.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文