【问题标题】:ARM assembly recursive power functionARM汇编递归幂函数
【发布时间】:2018-04-03 16:52:12
【问题描述】:

需要将以下C代码转换成ARM汇编子程序:

 int power(int x, unsigned int n) 
 {          
   int y; 
   if (n == 0)  
     return 1; 
   if (n & 1) 
     return x * power(x, n - 1); 
   else 
   { y = power(x, n >> 1); 
     return y * y; 
   } 
 }     

这是我目前所拥有的,但无法弄清楚如何让链接寄存器在每次返回后递增(不断循环回到同一点)

pow             CMP             r0, #0
                MOVEQ           r0, #1
                BXEQ            lr
                TST             r0, #1
                BEQ             skip
                SUB             r0, r0, #1
                BL              pow
                MUL             r0, r1, r0
                BX              lr
skip            LSR             r0, #1
                BL              pow
                MUL             r3, r0, r3
                BX              lr

【问题讨论】:

  • 递归函数需要一些堆栈使用...
  • 链接寄存器只有一个(把它想象成高级语言中的全局变量),即第一个子程序调用将耗尽它的存储潜力。对于进一步的子例程调用,您必须首先通过将链接寄存器值复制到其他存储中来“清空”它,然后调用子例程(它将用新的链接寄存器值替换),然后恢复以前的值。由于递归,可能的调用深度是无限的,因此您需要动态存储这些链接寄存器值。通常“堆栈内存”概念用于此类任务,请阅读一些教程。
  • 您还必须存储/恢复本地使用的任何其他寄存器值,因此递归子调用不会破坏您当前的值。通过代码的智能重新排列,您通常可以最小化需要保留的本地值 = 最小化每个嵌套调用的存储总量,提高最大深度的可能限制(受可用堆栈内存量的限制)。使用power 函数,您无需担心最大深度(如果n 是32 位,则32 是最大深度,即使您不小心使用它也应该适合堆栈)。
  • @EugeneSh。实现这一点的明智方法是尾递归,它需要多个堆栈帧,并且可以在默认 ARM 调用约定中仅使用调用者保存的寄存器来完成。
  • @EOF 那将是迭代的。

标签: c recursion assembly arm


【解决方案1】:

BL 指令不会自动从堆栈中压入或弹出任何内容。这节省了内存访问。这是它与 RISC 处理器一起工作的方式(部分原因是它们提供了 30 个通用寄存器。)

STR   lr, [sp, #-4]!   ; "PUSH lr"
BL    pow
LDR   lr, [sp], #4     ; "POP lr"

如果你重复一个BL 调用,那么你想在循环外的堆栈上调用STR/LDR

【讨论】:

  • 哪种 RISC架构提供30个寄存器?不是 SPARC,不是 MIPS,当然也不是 ARM。
  • 在 32 个中,有 2 个用于您无法以任何方式更改的事物,因此您只能使用 30 个。通常还有 1 或 2 个用于您无法更改的事物也可以更改,但这些寄存器仍然可用。
  • SPARC 具有可变数量的寄存器,可在寄存器窗口中使用。 MIPS 确实有 31 个 GPR。 32 位 ARM 拥有高达 15 个 GPR(其中一些已为例外情况预留)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-05
相关资源
最近更新 更多