64位iOS(A64)上的汇编器

Eta*_*tan 5 assembly calling-convention ios arm64 swift

我正在尝试将某些方法替换为asm实现。目标是iOS(iPhone 5S或更高版本)上的arm64。我想使用一个专用的汇编文件,因为内联汇编程序会带来额外的开销,并且与A64内存偏移量一起使用非常麻烦

互联网上没有太多的文档,因此我不确定该怎么做。因此,我将描述将功能移至ASM所遵循的过程。


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

UInt256.h

@import Foundation;

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

bool eq256(const UInt256 *lhs, const UInt256 *rhs);
Run Code Online (Sandbox Code Playgroud)

Bridging-Header.h

#import "UInt256.h"
Run Code Online (Sandbox Code Playgroud)

Reference implementation (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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

我发现的资源


问题

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

  1. 在asm文件中:.align似乎是为了优化-这真的有必要吗?如果是,则要对齐的正确值是什么?

  2. 是否有任何资料可以清楚地说明我的特定函数签名的调用约定?

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

    b。我怎么知道传递输出是正确的x0

    C。我怎么知道安全无事x9- x12状态寄存器?

    d。当我从C而不是Swift调用函数时,是否以相同的方式调用该函数?

  3. 什么是“间接结果位置寄存器”平均为r8ARM的文档中的寄存器的描述?

  4. 我还需要其他汇编程序指令.globl吗?

  5. 当我设置断点时,调试器似乎在实际位置上感到困惑,显示错误的行等。我做错什么了吗?

Ros*_*dge 5

  1. .align 2指令是程序正确性所必需的。A64 指令需要在 32 位边界上对齐。
  2. 您链接的文档对我来说似乎很清楚,不幸的是,这不是寻求建议的地方。
    • 您可以确定寄存器lhsrhs获取存储在X0X1通过遵循5.4.2节的指示通过(参数传递规则)的过程调用标准的为你链接的ARM 64位架构(AArch64)文件。由于参数都是指针,因此唯一适用的特定规则是 C.7。
    • 您可以按照第 5.5 节(结果返回)中的说明确定使用哪个寄存器返回值。这只是让您遵循与参数相同的规则。由于该函数返回一个整数,因此仅适用规则 C.7,因此该值在 X0 中返回。
    • 更改寄存器 X9 到 X12 中存储的值是安全的,因为它们在第 5.1.1 节(通用寄存器)中给出的表中被列为临时寄存器
    • 问题实际上是该函数在 Swift 中的调用方式是否与在 C 中的调用方式相同。过程调用标准文档和您链接的 Apple 特定异常文档都是根据 C 和 C++ 定义的。大概 Swift 遵循相同的约定,但我不知道 Apple 是否在任何地方明确说明了这一点。
  3. R8 的用途在第 5.5 节(结果返回)中描述。当返回值太大而无法放入用于返回值的寄存器时使用它。在这种情况下,调用者为返回值创建一个缓冲区并将其地址放入 R8。然后该函数将返回值复制到该寄存器中。
  4. 我认为您的示例汇编程序中不需要任何其他内容。
  5. 你问的问题太多了。您应该发布一个单独的更详细的问题来描述您的问题。

我应该说使用内联汇编编写代码的一个优点是您不必担心任何这些。类似以下未经测试的 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;
}
Run Code Online (Sandbox Code Playgroud)

好吧,也许它有点笨拙。