【问题标题】:Use registers without preserving them? (assembly)使用寄存器而不保留它们? (部件)
【发布时间】:2016-02-09 18:05:23
【问题描述】:

Windows调用转换后,只有RAX、RCX、RDX、r8和r9可以不用保存(称为volatile),其余的称为nonvolatile(必须保存)。

使用非易失性寄存器而不保留它们有什么问题吗? (我经常这样做,到目前为止我还没有遇到任何问题)

调用约定说只有 RCX、RDX、r8 和 r9 用于传递整数参数,额外的应该在堆栈上传递。

使用 r10 作为第五个参数而不是堆栈有什么缺点吗? (知道内存至少比寄存器慢100倍,我尽量用好寄存器)

当有十几个额外的寄存器时,为什么调用约定会在 4 个寄存器处停止以传递参数?

【问题讨论】:

  • 大多数函数平均可能有 4 个或更少的参数(只是猜测)。如果您使用所有可用的寄存器作为参数(并且您的函数有很多参数),那么您必须先保存寄存器的内容(例如在堆栈上),然后才能在函数中使用它们。 64 位 ABI 开发人员可能正在寻求平衡。一些寄存器用于加快对函数参数的访问,但保留寄存器可用,而无需保存它们。
  • 如果您使用非易失性寄存器而不保留它们,那么您的程序似乎可以正常工作是幸运的。这是非常糟糕的做法,可能会导致以后出现奇怪而奇妙的行为。如果寄存器被标记为非易失性,请为未经证实的全能实体或下一个可能必须维护您的代码的开发人员的爱 - 保留非易失性寄存器!
  • @Henri:他们的状态只有在你回来时才重要。推送/弹出您在函数开始/结束时使用的那些。另请参阅stackoverflow.com/tags/x86/info:“学习资源”链接之一是关于为什么有些寄存器是调用保留而有些不是的问题。 “volatile”在 C 中具有不同的技术含义,因此我不建议在这种情况下以这种方式使用它。 Linux 64 位调用约定有更多的临时(调用破坏)寄存器,我认为这可能会导致整体代码更好。我不确定在这方面是否有很多共识。
  • 无论您做什么,请确保生成正确的展开代码,以便操作系统在发生异常时执行正确的操作。
  • 如果您在函数中使用易失性寄存器,并且需要在必须进行的函数调用中保留它,那么您可能必须在函数调用周围保存/恢复它。如果您的函数不调用其他函数,这不是问题。如果您确实调用了其他函数,则调用者(您的函数)有责任保留可能需要在 CALL 中保存的任何寄存器

标签: windows assembly x86-64 cpu-registers win64


【解决方案1】:

大声写出来作为答案,因为我认为需要强调@Michael Petch(和其他人)的观点,以供将来参考,而不仅仅是评论。

违反ABI 是不是不好的做法,这是完全错误的
就像延迟悬空指针、写入 const 内存等错误一样。


背后的原因很明显:调用者通常会在非易失性寄存器中保存临时数据,它可以是字符串的索引、总和、加密哈希或其他任何东西。


如果您控制 ABI,则您制定规则;如果你不这样做,你就不会。

【讨论】:

    【解决方案2】:

    您“需要”保留非易失性寄存器,以便函数的调用者可以依赖它来保持这些寄存器中的值不变。

    另外,想一想,如果你编写一个使用这些寄存器而不是堆栈作为参数的函数,那么调用者必须在哪里存储他的值? 堆栈,在某些时候无法绕过它。

    您确实可以将非易失性寄存器推送到堆栈中,然后在需要时将它们弹出。

    【讨论】:

      猜你喜欢
      • 2015-03-27
      • 2011-04-24
      • 1970-01-01
      • 2011-10-15
      • 1970-01-01
      • 1970-01-01
      • 2019-01-09
      • 2016-03-28
      相关资源
      最近更新 更多