【问题标题】:ARMv6 Best Practices for Register Use in Function函数中寄存器使用的 ARMv6 最佳实践
【发布时间】:2014-09-07 00:29:12
【问题描述】:

Assembly 的总数为 n00b,但我觉得我已经掌握了窍门。不过,我有一个关于在函数中使用寄存器的最佳实践的问题。

据我了解:在 ARM11 上的 13 个可用通用寄存器中,按照惯例,寄存器 0-3 用于传递参数(0 和 1 也用于返回值),而 4-12用于在函数执行期间存储工作值。

但是,我也看到了代码示例,其中人们也使用寄存器 0-3 来存储工作值,只要它们中的任何一个可用,因为它们不需要将前一个值推送和弹出到堆栈上。

虽然我可以理解为什么有人可能想要避免额外的推送和弹出步骤,但似乎将 r0-r3 用于除了将值传入和传出函数之外的任何事情都可能会导致问题(因为你有不保证您调用的任何函数都会保留它们的值)。

那么这里的最佳做法是什么?我什么时候应该(如果有的话)使用寄存器 0-3 作为工作值,什么时候应该使用寄存器 4-12?

【问题讨论】:

  • 在纯汇编中,“遵循既定的调用约定”和“随心所欲”之间并没有真正的区别,但显然“最佳实践”的概念仅适用于其中之一。
  • 很公平。您对我如何改进它或避免犯这个错误有什么建议吗?还是您只是认为应该从文档中收集到一些东西?
  • 关于“收集文档”,这些事情很难,除非它是您日常工作的一部分。您应该始终将文档放在手边,了解其中包含的内容并在需要澄清时检查它们。

标签: assembly arm cpu-registers armv6


【解决方案1】:

似乎使用 r0-r3 除了传递值 并且超出功能可能会导致问题(因为您 无法保证您调用的任何函数都会保留其 值)。

这正是您可以使用 r4-r11 的时候,因为 ABI 指定被调用者必须保留这些值:)

寄存器 r0-r3 是调用者保存的,因此调用者必须确保在函数调用之前保存这些寄存器中存储的任何重要值。作为被调用者,你可以对这些寄存器做任何你想做的事情。

【讨论】:

  • 你真的需要保存r12吗? r4-r12 有 9 个寄存器。这是一个奇数。不是计算机世界的最爱。
  • 这就是我一直在阅读的 Raspberry Pi 特定指南要说的内容。不确定这是否是 ARMv6 实现的标准,或者这只是我的文档中的一个错误。
  • @auselen 32 位 ARM 有 13 个通用寄存器,这是一个奇数,仍然是计算机界的最爱
  • r12 不是在 AAPCS 下保存的被调用者 - 如果需要隐藏远 (>32MB) 调用,链接器可能会损坏它。
  • @LưuVĩnhPhúc 你似乎不明白这一点。他的回答错了。
【解决方案2】:

... 似乎将 r0-r3 用于除了将值传入和传出函数之外的任何事情都可能导致未来出现问题(因为你不能保证你调用的任何函数都会保留它们的值)。

寄存器比内存快,寄存器比二级缓存快,寄存器比一级缓存快,寄存器快。通过使用 R4-R8,您正在创建额外的存储和加载。在手工编码的汇编器中,这将创建额外的指令。对于 ARM 叶汇编程序函数,没有序言,结尾是 bx lr。多么简单。

您的陈述似乎将 r0-r3 用于除了传递值之外的任何事情对于许多算法和函数来说都是不正确的。考虑一个 GCD 实现,

int gcd(int a, int b)
{
   while(a!=b)
     if(a>b)
       a = a - b;
     else
       b = b - a;
   return a;
}

参数ab在算法过程中不断更新。第一次迭代后不再需要原始的 ab 值。这个事实在编译器优化中众所周知为Static single assignment form。寄存器重命名为a0、a1

所以输入参数通常不需要保留。无需将它们复制到 r4-r8 并强制生成堆栈帧。 ARM 编译器将努力这样做。无需人类手动编写代码。如果必须这样做,除非您正在学习,否则最好让编译器生成代码。来自 David Seal 的 ARM ARM 的 ARM gcd 算法是,

gcd: cmp r0, r1
     subgt r0, r0, r1
     sublt r1, r1, r0
     bne gcd
     bx  lr

例程是五个指令。如果你保存了输入参数,你会加倍例程的大小。

gcd:
   stmfd sp!, {r4, r5}   ; extra code plus two data
   mov r4, r0            ; extra code
   mov r5, r1            ; extra code

1: cmp r4, r5
   subgt r4, r4, r5
   sublt r5, r5, r4
   bne 1b

   mov r0, r4            ; extra code to setup return
   ldmfd sp!, {r4, r5}   ; extra code plus two data
   bx  lr

对于少量输入,您可以将执行时间延长三倍。在没有额外代码的情况下,也可以说更容易理解汇编程序。你永远不应该保存你不使用的寄存器。对于生产质量和专业代码,使用 r1r3 并先将它们存储在堆栈中总是有意义的。

【讨论】:

  • 注意:必须保存并可以安全使用的寄存器是r4-r8。对于 r8 上的任何寄存器,您确实需要更多地了解您的系统才能使用它们。在汇编程序中,没有任何限制,如果您愿意,您甚至可以使用 r13/sp/stack 指针进行计算。破坏 AAPCS 将对如何与编译器代码进行互操作有一定的限制。
  • 哇,谢谢!这确实有助于正确看待事物。
  • 一个不清楚的地方是,如果您的例程只使用两个参数 (a,b),那么寄存器 R2 和 R3 仍然可以免费使用,无需保存。因此,您可以删除第二个示例中的 stmldm 指令,并将 r4,r5 替换为 r2,r3。如果可能,最好将它们保存在 r0,r1 中。不需要mov 指令。使用不同的例程/功能,r2,r3 可能有用。
猜你喜欢
  • 2014-12-10
  • 2020-11-04
  • 1970-01-01
  • 2012-05-04
  • 1970-01-01
  • 1970-01-01
  • 2015-09-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多