LDM:我最喜欢的手臂指令

2020-10-15 22:34:03

☰Vladimir Keleshev·2020-10-13LDM-或load Multiple-是我最喜欢的ARM指令集汇编指令。原因如下。

这里,它需要一个基址寄存器(在本例中为R4)和一个寄存器集(在本例中为{R0,R1,R2,R3})。它将来自基址寄存器中的地址的连续字加载到集合中的寄存器中。在此示例中,可以使用以下类似C的伪代码来描述效果:

对于一条指令来说,这是相当多的作业!这就是为什么它被称为加载倍数。

SET符号还允许范围。我们可以重写前面的示例,如下所示:

组中允许任意和所有16个ARM寄存器。因此,以下内容是合法的:

LDM R0,{R0,R1,R2,R3,R4,R5,R6,R7,R8,R9,R10,R11,R12,R13,R14,R15}。

寄存器集被编码为32位指令中的16位掩码。以下是原始示例的简化编码:

这样的指令非常适合像ARM这样的加载-存储体系结构,其中主要工作流程是:

有了这两个功能,您可以快速复制大块内存。您可以复制8个字(或32个字节!)。只需两条指令即可完成内存:

LDM和STM也有自动增量变量(用“!”表示)。其中基址寄存器按加载/存储的字数递增,以便您可以在快速循环中进行复制:

ARM的POP指令只是带有堆栈指针(和自动递增)的LDM的别名。以下两项是完全相同的:

您可以一次将大量数量推送到堆栈或从堆栈中弹出。如果用另一个寄存器替换SP,就可以在内存的其他区域实现高效堆栈。例如,您可以在堆中实现影子堆栈。

您是否因为需要保存调用保留寄存器而犹豫是否要使用调用保留寄存器,而且不管怎样,您还不如使用堆栈槽呢?再也不会了,因为您可以一次性保存所有要使用的呼叫保留寄存器:

在ARM上,前四个参数、返回地址(LR)和帧指针(FP)都传入寄存器。这就是为什么有有效的开场白和结束语特别重要的原因。幸运的是,您可以使用相当标准的ARM序言一次性保存FP和LR:

这是通过将返回地址值(LR)弹出到程序计数器寄存器(PC)来实现的,因此您不需要显式返回!

这本身就足够好了,但是您可以(同时)将一些参数溢出到堆栈上(例如,如果它们的地址被获取):

或者,您可以节省fp和lr,同时在堆栈上分配一些空间:

在这种情况下,我们推送R0-R3不是为了它们的值,而是为了将堆栈指针前进四个字。

我怀疑这是一个艰难的权衡,但当设计64位版本的ARM指令集时,决定将寄存器数量增加一倍,达到32个。我记得读过一篇文章说,这一改变全面提高了大约6%的性能。对于32个寄存器,不再可能将所有寄存器的位掩码编码为32位长指令。因此,ARM64具有LDP和STP:Load Pair和Store Pair,它们是LDM和STM的精神继承者。

你喜欢这篇博文吗?如果是这样的话,请阅读我的新书:从头编译到汇编。它教会您足够的汇编语言编程和编译器基础知识来实现小型编程语言的编译器。