在ARM程序集中创建函数时,我通常会在开始时将LR
寄存器的内容推送到r4-r5
,在函数完成后我将r4-r5
弹出到PC
:
.global myfunc
.type myfunc, %function
myfunc:
push {r4-r5,lr}
... do stuff...
pop {r4-r5,pc}
但是,我已经读过使用stmfd
和ldmfd
可能会获得更好的效果:
myfunc:
stmfd sp!,{r4-r11,lr}
...do stuff...
ldmfd sp!,{r4-r11,pc}
sp
究竟是什么?我认为保存所有寄存器r4-r11
并不值得我在myfunc
内实际使用它们,对吧?那么push-pop变体在这种情况下会更好吗?
答案 0 :(得分:6)
PUSH {...}
是ARM指令STMDB SP!,{...}
POP {...}
是ARM指令LDMIA SP!,{...}
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}
您可以看出sp
是r13
的别名,而lr
是r14
的别名。其中,sp
代表堆栈指针,lr
代表链接寄存器。
答案 3 :(得分:0)
SP
是堆栈指针寄存器 - 指示当前堆栈的顶部。我相信如果你要保存更高的寄存器,你只需要使用stmfd
。如果您只需要保存几个较低的寄存器,只需按下即可。流行。