让我们使用我的答案作为在 Android 和 iOS 上编写 ARM64 代码的一般指南。首先,我们将从挥发性和非挥发性寄存器(维基百科):
X0-X7 - 参数和返回值(易失性)
X8 = 间接结果(结构)位置(或临时寄存器)
X9-X15 = 临时(易失性)
X16-X17 - 介绍调用使用寄存器(PLT、链接器)或临时值
X18 - 平台特定使用 (TLS)
X19-X28 - 被调用者保存的寄存器(非易失性)
X29——帧指针
X30——链接寄存器(LR)
SP - 堆栈指针和零(XZR)
V0-V7、V16-V31 - 易失性 NEON 和 FP 寄存器
V8-V15 - 被调用者保存的寄存器(非易失性,编译器用于临时变量)
接下来是为代码正确创建“段”的汇编器指令:
Android
.cpu通用+fp+simd
.text
对于每个函数,添加以下 3 行
.section .text.MyFunctionName,"ax",%progbits
.对齐2
.type MyFunctionName, % 函数
iOS(除了对齐指令之外,没有什么真正需要的)
.对齐2
声明公共(全局)标签
Android
.global MyFunctionName
iOS
.global 函数名
下一个区别是获取指向源代码中定义的静态数据的指针。例如,假设您有一个数据表,并且您希望向寄存器 X0 加载指向该表的指针。
Android
adrp x0, MyDataTable
add x0, x0, #:lo12:MyDataTable
iOS
adrp x0,MyDataTable@PAGE
add x0,x0,MyDataTable@PAGEOFF
接下来是 NEON 语法。 iOS允许将大小信息附加到指令助记符中,而Android则希望看到带有大小后缀的寄存器
Android
ld1 {v0.16b},[x0],#16
iOS
ld1.16b {v0},[x0],#16
嵌套循环
在 32 位 ARM 代码中,通常将 LR 压入堆栈,以在需要从函数内调用函数时保留它。由于 NEON 指令不再位于协处理器中,并且已合并到 Aarch64 的主指令集中,因此来回移动数据不会受到任何影响。现在可以将 X30 (LR) 保存在未使用的 NEON 寄存器中。例如:
fmov d0,x30 // preserve LR
<some code which makes function calls>
fmov x30,d0 // restore LR
目前为止就这样了。如果有人发现有更多差异的具体情况,我会添加它们。