64位iOS上的汇编程序(A64)

我正在尝试用asm-implementation替换某些方法.目标是iOS上的arm64(iPhone 5S或更新版本).我想使用一个专用的汇编程序文件,因为内联汇编程序带来了额外的开销,并且非常强大.

互联网上没有太多关于此的文档,所以我不确定我是如何做到的.因此,我将描述将函数移动到ASM所遵循的过程.

该问题的候选函数是256位整数比较函数.

UInt256.h

@import Foundation;

typedef struct {
    uint64_t value[4];
} UInt256;

bool eq256(const UInt256 *lhs, const UInt256 *rhs);

转职Header.h

#import "UInt256.h"

参考实现(Swift)

let result = x.value.0 == y.value.0
          && x.value.1 == y.value.1
          && x.value.2 == y.value.2
          && x.value.3 == y.value.3

UInt256.s

.globl _eq256
.align 2
_eq256:
    ldp        x9, x10, [x0]
    ldp       x11, x12, [x1]
    cmp        x9, x11
    ccmp      x10, x12, 0, eq
    ldp        x9, x10, [x0, 16]
    ldp       x11, x12, [x1, 16]
    ccmp       x9, x11, 0, eq
    ccmp      x10, x12, 0, eq
    cset       x0, eq
    ret

我找到的资源

> Procedure Call Standard for the ARM 64-bit Architecture (AArch64)文件的第5.1.1节解释了程序调用期间每个寄存器的用途.
> iOS特定deviations.
> iOS Assembler Directives.

问题

我使用XCTest测试了代码,创建了两个随机数,在它们上运行Swift和Asm实现,并验证两者都报告相同的结果.代码似乎是正确的.

>在asm文件中:.align似乎是为了优化 – 这是否真的有必要,如果是,那么对齐的正确值是什么?
>是否有任何来源可以清楚地解释我的特定函数签名的调用约定是什么?

一个.我怎么知道输入实际上是通过x0和x1传递的?

湾我怎么知道在x0中传递输出是正确的?

C.我怎么知道clobber x9-x12和状态寄存器是安全的?

d.当我从C而不是Swift调用它时,该函数是否以相同的方式调用?
>“间接结果位置寄存器”对ARM文档中的r8寄存器描述意味着什么?
>除了.globl之外,我还需要其他任何汇编程序指令吗?
>当我设置断点时,调试器似乎在实际位置混淆,显示不正确的行等.我做错了什么?

最佳答案 > .align 2指令是程序正确性所必需的. A64指令需要在32位边界上对齐.

>您链接的文档对我来说似乎很清楚,不幸的是,这不是要求推荐的地方.

>您可以按照您链接的ARM 64位体系结构(AArch64)文档的过程调用标准的5.4.2节(参数传递规则)中给出的说明,确定寄存器lhs和rhs是否存储在X0和X1中.由于参数都是指针,因此唯一适用的特定规则是C.7.
>您可以按照第5.5节(结果返回)中给出的说明确定使用哪个寄存器返回值.这只是遵循与参数相同的规则.由于函数返回一个整数,因此仅适用规则C.7,因此该值在X0中返回.
>更改存储在寄存器X9到X12中的值是安全的,因为它们在第5.1.1节(通用寄存器)中给出的表中列为临时寄存器
>问题在于是否在Swift中调用函数与在C中调用函数相同.过程调用标准文档和您链接的Apple特定异常文档都是根据C和C定义的.据推测Swift遵循相同的惯例,但我不知道Apple是否在任何地方明确表达了这一点.

> R8的目的在5.5节(结果返回)中描述.当返回值太大而无法放入用于返回值的寄存器时,会使用它.在这种情况下,调用者为返回值创建一个缓冲区并将其地址放在R8中.然后,该函数将返回值复制到该寄存器中.
>我不相信你的示例汇编程序中还需要其他任何东西.
>你问了太多问题.您应该发布一个单独的,更详细的问题来描述您的问题.

我应该说使用内联汇编编写代码的一个好处是你不必担心任何这一点.像以下未经测试的C代码一样不应该太笨重:

bool eq256(const UInt256 *lhs, const UInt256 *rhs) {
     const __int128 *lv = (__int128 const *) lhs->value;
     const __int128 *rv = (__int128 const *) rhs->value;

     uint64_t l1, l2, r1, r2, ret;

     asm("ldp       %1, %2, %5\n\t"
         "ldp       %3, %4, %6\n\t"
         "cmp       %1, %3\n\t"
         "ccmp      %2, %4, 0, eq\n\t"
         "ldp       %1, %2, %7\n\t"
         "ldp       %3, %4, %8\r\n"
         "ccmp      %1, %3, 0, eq\n\t"
         "ccmp      %2, %4, 0, eq\n\t"
         "cset      %0, eq\n\t",
         : "=r" (ret), "=r" (l1), "=r" (l2), "=r" (r1), "=r" (r2)
         : "Ump" (lv[0]), "Ump" (rv[0]), "Ump" (lv[1]), "Ump" (rv[1])
         : "cc")

     return ret;
}

好吧,也许它有点笨拙.

点赞