从ARM程序集中的过程返回

时间:2013-03-23 12:59:14

标签: assembly arm

在ARM程序集中创建函数时,我通常会在开始时将LR寄存器的内容推送到r4-r5,在函数完成后我将r4-r5弹出到PC

.global myfunc
.type   myfunc, %function

myfunc:
push {r4-r5,lr}
... do stuff...
pop {r4-r5,pc}

但是,我已经读过使用stmfdldmfd可能会获得更好的效果:

myfunc:
stmfd sp!,{r4-r11,lr}
...do stuff...
ldmfd sp!,{r4-r11,pc}

sp究竟是什么?我认为保存所有寄存器r4-r11并不值得我在myfunc内实际使用它们,对吧?那么push-pop变体在这种情况下会更好吗?

4 个答案:

答案 0 :(得分:6)

PUSH {...}是ARM指令STMDB SP!,{...}

的Thumb等效物

POP {...}是ARM指令LDMIA SP!,{...}

的Thumb等效项

STM 表示存储多个。
DB 表示减少之前,即在这种情况下减少每个商店之前的目的地地址 IA 表示递增后,即在每种加载后递增源地址 表示将最终地址写回源/目标地址寄存器。例如,如果SP为0x100且您STMDB SP!,{R0-R2},则SP之后您将获得0xF4。
SP R13的别名,用作ARM处理器上的堆栈指针。

答案 1 :(得分:3)

push和pop是汇编程序的伪指令,它们不是真正的指令。您要么获得一个商店,基本寄存器更新为stm。

push {r11}
stmdb r13!,{r11}

push {r10-r12}
stmdb r13!,{r10-r12}

我更喜欢stmdb到stmfd只是同一条指令的不同语法。 (stmdb和ldmia对我来说很有意义,之前递减,之后递增)。

汇编然后反汇编。

   0:   e52db004    push    {fp}        ; (str fp, [sp, #-4]!)
   4:   e92d0800    stmfd   sp!, {fp}
   8:   e92d1c00    push    {sl, fp, ip}
   c:   e92d1c00    push    {sl, fp, ip}

如果您查找stm编码或者甚至只是查看这些位并考虑它,指令0xe92d的高位是stmia / fd,低位是指示哪些寄存器要保存的标志,通知地址4这是11的推,然后在8和c你有那个位设置r11,然后是它下面的那个r10和它上面的那个r12。

推送和弹出比尝试记住使用sp并使用更容易阅读!在注册之后记住ia / db / fd等后缀和所有这些。

我相信拇指可能有实际的推/弹。

arm的单个寄存器变体变成了单个存储,如果你使用带有一条指令或str的stm,则无关紧要,操作在功能上是等效的。

只要在操作后更新r13并使用db或fd作为stm,就可以使用伪指令或实际指令。

如果您要存储/恢复多个寄存器,那么一定要在一条指令中列出它们,不要列出几个推送或弹出

no:
push {r10}
push {r11}
push {r12}
yes:
push {r10-r11}

除非用拇指,否则您可能没有选择,因为您只能按r0-r7 + r14并弹出r0-r7 + r15以保存更高的寄存器,您必须将它们复制到较低的寄存器然后使用push。你必须使用push stm不会让你使用r13。 (thumb2取决于您的架构可用的扩展,为您提供更多类似于手臂的体验)。

重读你的问题

sp是r13,堆栈指针。伪指令选择正确的指令,所以你不必担心stm vs str。当您存储多个寄存器时,您“可以”对现代臂系统进行优化,但不能保证。如果您的amba / axi总线是64位宽,则一次写入64位比一次写入32位快2倍以上,因为在64位内存系统上需要进行读取 - 修改 - 写入32位写入,但64位写入不会(让我们忽略缓存行为)。如果stm在一个对齐的地址上(当使用堆栈时需要太多的代码来解决这个问题,不要担心它)然后推送2个寄存器会明显快于两个单独的推送(除非核心将这些优化为一个公共汽车周期)。如果你推4个寄存器,如果未对齐则会发生三个事件中的一个,那么你会在未对齐的地址上获得三次传输32位传输(比如说0x1004),然后在对齐的地址之后进行64位传输(0x1008),然后是32最后一个寄存器的位传输(0x1010)。如果四个寄存器推送已经在分配地址上,那么两个事件中的一个发生两个单独的64位传输两个寄存器到0x2010让我们说两个到0x2018或长度为2个传输(一次传输中有两个64位项)对齐的基址,比如0x2010。你不会得到最糟糕的情况,虽然这是四个单独的32位传输,所以值得使用stm / push。

答案 2 :(得分:1)

如果您不打算使用它们,则无需将寄存器压入堆栈。话虽如此,您将不得不看看是否会增加任何真正的性能优势。我认为,推送所有内容很简单,因为如果你或某人修改了代码,它将不会意外地破坏寄存器和堆栈。

顺便说一句,你也可以这样做;也就是说,使用stmfd仅保存r4-r5。

myfunc:
stmfd sp!,{r4-r5,lr}
...do stuff...
ldmfd sp!,{r4-r5,pc}

OR

myfunc:
stmfd r13!,{r4-r5,r14}
...do stuff...
ldmfd r13!,{r4-r5,pc}

您可以看出spr13的别名,而lrr14的别名。其中,sp代表堆栈指针,lr代表链接寄存器。

答案 3 :(得分:0)

SP是堆栈指针寄存器 - 指示当前堆栈的顶部。我相信如果你要保存更高的寄存器,你只需要使用stmfd。如果您只需要保存几个较低的寄存器,只需按下即可。流行。