【问题标题】:What is the fastest way to index into ARMv8 registers索引到 ARMv8 寄存器的最快方法是什么
【发布时间】:2020-11-02 03:01:27
【问题描述】:

ARMv8 指令集允许访问指令中内置的任何整数寄存器,如下所示:

add x0, x1, x2  @ x0 = x1 + x2, 64 bit arithmetic

但是,有没有办法从 0 到 15 加载寄存器,例如,使用寄存器中的值?

例如,假设寄存器 x16 包含数字 5。在这种情况下,我想要 x5。

这当然可以在内存(数组)中完成,但速度要慢得多。

ldr x19, [x17, x16, lsl #3]

其中 x17 是某个基地址,x16 是索引,但这需要进入内存。 如果缓存,这会更慢。如果写回该值,则写入可能需要更多时间。

我能想到的唯一其他方法是某种计算的 goto:

    add x18, x18, x16, lsl #6
    bx  x18
1:
    mov x19, x0
    ...

2:
    mov x19, x1
    ...

3:
    mov x19, x2
    ...

这甚至比数组访问还要慢。

理想情况下会有如下索引模式:

mov x19, x[x16]

【问题讨论】:

  • 没有办法索引寄存器集。如果您认为自己需要这种东西,请重新考虑您的方法。如果完成一个,我想一系列比较和条件选择可能是最快的实现。否则,将变量溢出到 RAM 中然后对其进行索引可能会更快。
  • 事实上,我们不久前对 x86 也有过同样的问题。也许the discussion there 可能对您有所帮助。 TL;DR:在内存中使用数组。
  • 如果这是XY problem 的一个实例,也许可以通过索引通用寄存器来找到解决您尝试解决问题的方法。

标签: assembly arm cpu-registers arm64


【解决方案1】:

如 cmets 中所述,使用内存中的数组为较小的数据集执行此操作通常更快。在 ARM 上,还可以使用表查找指令更有效地处理大量数据:

最多可以将四个 16 字节 SIMD 寄存器传输到 tbl 指令。对于条目的 16 个字节中的每一个,该值从具有相应编号的部分寄存器中获取,否则为零(但是,类似的指令 tbx 保持该值不变)。一个例子:

input:  v0 = [0x00, 0x01, 0x08, 0x10, 0x12, 0x20, 0x21, 0x30, 0x3F, 0x40, ...]
tables: v4 = [0x40, 0x41, 0x42, ..., 0x4F]
        v5 = [0x50, 0x51, 0x52, ..., 0x5F]
        v6 = [0x60, 0x61, 0x62, ..., 0x6F]
        v7 = [0x70, 0x71, 0x72, ..., 0x7F]

执行tbl v1.16b, {v4.16b, v5.16b, v6.16b, v7.16b}, v0.16b 会得到以下结果:

output: v1 = [0x40, 0x41, 0x48, 0x50, 0x52, 0x60, 0x61, 0x70, 0x7F, 0x00, ...]

使用tbx,所有大于0x3F 的值都将被导入而不是归零:

output: v1 = [0x40, 0x41, 0x48, 0x50, 0x52, 0x60, 0x61, 0x70, 0x7F, 0x40, ...]

如何使用它来索引寄存器?

由于只能进行逐字节查找,因此需要进行一些初步工作:通用寄存器中的索引被传输到 SIMD 寄存器并另外传输到第二个寄存器,以便它可以适应两个寄存器。

input:                x0 = [index, 0, 0, ..., 0]
first  SIMD register: v0 = [index*8, index*8+1, ..., index*8+7, 0, 0, ..., 0]
second SIMD register: v1 = [index*8-64, index*8-63, ..., index*8-57, 0, 0, ..., 0]

这是为了满足查找值必须始终介于 0 和 15(或 31、47 或 63)之间的事实,并且应在此处对八个连续字节进行查找。

因此,索引被转换为每个查找表中的一个位置(每个tbl 指令都有一个)。如果超出范围,tbl 将传递零,如果结果是 orr-ed 最后将无效。


工作示例:

需要定义以下数据:

modifier: .byte 0, 1, 2, 3, 4, 5, 6, 7, -64, -63, -62, -61, -60, -59, -58, -57

输入值在x0。查找的值取自 lookup_table 内存位置。结果存储在x0

// Load lookup table from memory
adr  x1, lookup_table
ldp  q8, q9, [x1]
ldp  q10, q11, [x1, 32]
ldp  q12, q13, [x1, 64]
ldp  q14, q15, [x1, 96]

// Take value to be looked up from general-purpose register
dup  v0.8b, w0

// Prepare index before lookup
adr  x1, modifier
ldp  d2, d3, [x1]
shl  v0.8b, v0.8b, 3
add  v2.8b, v0.8b, v2.8b
add  v3.8b, v0.8b, v3.8b

// Do Lookup
tbl  v2.8b, {v8.16b,  v9.16b,  v10.16b, v11.16b}, v0.8b
tbl  v3.8b, {v12.16b, v13.16b, v14.16b, v15.16b}, v1.8b
orr  v0.8b, v2.8b, v3.8b

// Load the result back into a general-purpose register
umov x0, v0.2d[0]

如果实在没有办法,也可以从通用寄存器x8x23取值:

ins   v8.2d[0], x8
ins   v9.2d[0], x10
ins  v10.2d[0], x12
//   ...
ins  v15.2d[0], x22
ins   v8.2d[1], x9
ins   v9.2d[1], x11
ins  v10.2d[1], x13
//   ...
ins  v15.2d[1], x23

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-04
    • 2019-05-29
    • 2021-09-07
    • 1970-01-01
    相关资源
    最近更新 更多