代码之家  ›  专栏  ›  技术社区  ›  CRThaze

函数中寄存器使用的ARMv6最佳实践

  •  2
  • CRThaze  · 技术社区  · 10 年前

    在Assembly中总共有n00b,但我觉得我已经掌握了它的窍门。然而,我对在函数中使用寄存器的最佳实践有疑问。

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

    然而,我也看到了一些代码示例,其中人们也使用寄存器0-3作为工作值,只要它们中的任何一个可用,因为它们不需要push&将上一个值弹出到堆栈中。

    虽然我能理解为什么有人会想避免额外的压力&弹出步骤,似乎除了将值传入和传出函数之外,使用r0-r3进行任何操作都可能导致后续问题(因为无法保证调用的任何函数都会保留其值)。

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

    2 回复  |  直到 10 年前
        1
  •  3
  •   tangrs    10 年前

    似乎除了在 功能失效可能会导致问题(因为 不能保证您调用的任何函数都会保留 值)。

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

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

        2
  •  1
  •   artless noise    10 年前

    …似乎使用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;
    }
    

    参数 a b 在算法期间不断更新。原件 b 在第一次迭代之后,永远不需要值。这一事实在编译器优化中众所周知 Static single assignment form 。寄存器重命名为 0 1.

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

    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
    

    对于小输入,您可以将执行时间增加三倍。可以说,在没有额外代码的情况下,更容易理解汇编器。你不应该保存你不使用的寄存器。对于生产质量,使用专业代码总是有意义的 r1 r3 放置到位,然后将其存放在堆栈上。