Каков самый быстрый способ индексирования в регистры ARMv8 - PullRequest
0 голосов
/ 13 июля 2020

Набор инструкций 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]

1 Ответ

3 голосов
/ 13 июля 2020

Как отмечалось в комментариях, часто быстрее работать с массивом в памяти, чтобы сделать это для небольших наборов данных. В 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 объединен вместе в конце.

Рабочий пример:

Необходимо определить следующие данные:

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]

Если другого пути действительно нет, значения также можно взять из регистров общего назначения x8 в x23:

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
...