Какой правильный порт ARMv7-ARMv8 NEON для этого кода умножения матриц? - PullRequest
0 голосов
/ 08 марта 2019
    // http://infocenter.arm.com/help/topic/com.arm.doc.dai0425/DAI0425_migrating_an_application_from_ARMv5_to_ARMv7_AR.pdf
// p. 4-21

.macro mul_col_f32 res_q, col0_d, col1_d
vmul.f32 \res_q, q8, \col0_d[0] @ multiply col element 0 by matrix col 0
vmla.f32 \res_q, q9, \col0_d[1] @ multiply-acc col element 1 by matrix col 1
vmla.f32 \res_q, q10, \col1_d[0] @ multiply-acc col element 2 by matrix col 2
vmla.f32 \res_q, q11, \col1_d[1] @ multiply-acc col element 3 by matrix col 3
.endm

// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.100748_0606_00_en/lmi1470147220260.html
// http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203j/Cacjfjei.html

.globl  mat44mulneon
.p2align 2 // what's this ?
.type mat44mulneon,%function
mat44mulneon:
.fnstart // not recognized by eclipse syntax coloring?
// ---------
vld1.32 {d16-d19}, [r1]! @ load first eight elements of matrix 0
vld1.32 {d20-d23}, [r1]! @ load second eight elements of matrix 0
vld1.32 {d0-d3}, [r2]! @ load first eight elements of matrix 1.
vld1.32 {d4-d7}, [r2]! @ load second eight elements of matrix 1.
mul_col_f32 q12, d0, d1 @ matrix 0 * matrix 1 col 0
mul_col_f32 q13, d2, d3 @ matrix 0 * matrix 1 col 1
mul_col_f32 q14, d4, d5 @ matrix 0 * matrix 1 col 2
mul_col_f32 q15, d6, d7 @ matrix 0 * matrix 1 col 3
vst1.32 {d24-d27}, [r0]! @ store first eight elements of result.
vst1.32 {d28-d31}, [r0]! @ store second eight elements of result.
// ---------
bx lr // Return by branching to the address in the link register.
.fnend

Приведенный выше код, который я нашел на сайте ARM (см. Ссылки в комментариях), работает на моем компьютере ARM Cortex A9, т.е. на компьютере ARMv7.

Я сейчас пытаюсь получить этоработать на процессоре ARMv8 / aarch64.Я нашел этот слайд: портирование на ARM64

В самом конце он показывает код умножения матриц.Но он использует циклы, и я думаю (поправьте меня, если я не правильно понимаю) код, который я разместил, будет быстрее, если будет перенесен на новую мнемонику ARMv8.Связанный документ также показывает некоторые изменения v7 -> v8, поэтому, например, я изменил такие вещи, как vmul.32, на fmul и так далее.Названия регистров, приведенные в примере, не совпадают с именами в приведенном выше коде.Так как я не очень хорошо разбираюсь в ARM, я не знаю, какие здесь эквиваленты.Например, когда я строю свой проект, я получаю сообщение об ошибке вроде:

operand 1 must be a SIMD vector register list -- `st1 {d24-d27},[r0]

Я не уверен, что это будет единственной проблемой, поэтому я, скорее, спрашиваю: какие изменения необходимо внести в кодработать на машине aarch64?

1 Ответ

1 голос
/ 15 марта 2019

Вот примерная версия этой подпрограммы AArch64:

.macro mul_col_f32 res, col
    fmul \res, v16.4s, \col[0] // multiply col element 0 by matrix col 0
    fmla \res, v17.4s, \col[1] // multiply-acc col element 1 by matrix col 1
    fmla \res, v18.4s, \col[2] // multiply-acc col element 2 by matrix col 2
    fmla \res, v19.4s, \col[3] // multiply-acc col element 3 by matrix col 3
.endm

.globl  mat44mulneon
mat44mulneon:
    ld1 {v16.4s, v17.4s, v18.4s, v19.4s}, [x1] 
    ld1 {v0.4s,  v1.4s,  v2.4s,  v3.4s},  [x2] 
    mul_col_f32 v24.4s, v0.s // matrix 0 * matrix 1 col 0
    mul_col_f32 v25.4s, v1.s // matrix 0 * matrix 1 col 1
    mul_col_f32 v26.4s, v2.s // matrix 0 * matrix 1 col 2
    mul_col_f32 v27.4s, v3.s // matrix 0 * matrix 1 col 3
    st1 {v24.4s, v25.4s, v26.4s, v27.4s}, [x0] 
ret

Несколько неполных заметок о преобразовании, помимо общих вещей, упомянутых в связанной презентации:

  • Вы можете загрузить до 64 байтов одной инструкцией ld1, по сравнению с 32 байтами с vld1 в AArch32. Это исключает необходимость увеличения указателей r0 / r1 / r2 или x0 / x1 / x2
  • Я пропустил специфичные для ОС / двоичного формата .fnstart, .fnend и .type, их можно читать в тех же местах, что и в оригинальной версии, если это необходимо
  • Для сборки AArch64 @ больше не является символом комментария
  • Параметр col для mul_col_f32 имеет форму v0.s, в отличие от v0.4s. При выборе конкретной полосы, после объединения с суффиксом [0] в макросе, нужно пропустить количество полос, например, чтобы выбрать первую полосу регистра v0.4s, она должна быть записана как v0.s[0]. Ассемблер GNU допускает v0.4s[0], но другие ассемблеры (как встроенный ассемблер Clang / LLVM, так и armasm64 от Microsoft) допускают только прежний синтаксис.
...