此矩阵乘法代码的正确 ARMv7 到 ARMv8 NEON 端口是什么?
What's the correct ARMv7 to ARMv8 NEON port of this matrix multiplication code?
// 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 CPU 上将其添加到 运行。
我找到了这张幻灯片:
porting to ARM64
最后显示的是矩阵乘法代码。但它使用循环,我猜想(如果我没看清这一点,请纠正我)如果移植到新的 ARMv8 助记符,我发布的代码会更快。
链接文档还显示了一些 v7 -> v8 更改,例如我将 vmul.32 更改为 fmul 等等。示例中给出的寄存器名称与上面发布的代码中的名称不匹配。由于我并不完全精通任何 ARM asm,所以我不知道这里有什么等价物。
例如。当我构建我的项目时,出现如下错误:
operand 1 must be a SIMD vector register list -- `st1 {d24-d27},[r0]
不过,我不确定这是唯一的问题,所以我想问:
在 aarch64 机器上 运行 的代码需要做哪些更改?
这是该例程的粗略 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
除了链接演示文稿中提到的一般事项外,关于转换的一些不全面的说明:
- 与 AArch32 中的
vld1
相比,您可以使用一条 ld1
指令加载最多 64 个字节。这避免了增加 r0/r1/r2 或 x0/x1/x2 指针 的需要
- 我省略了 OS/binary 特定格式
.fnstart
、.fnend
和 .type
,如果需要,可以在与原始版本相同的位置重新添加它们
- 对于 AArch64 程序集,
@
不再是注释字符
-
mul_col_f32
的 col
参数采用 v0.s
形式,与 v0.4s
相反。选择特定车道时,在宏中与 [0]
后缀连接后,应省略车道数,例如要选择 v0.4s
寄存器的第一通道,应写为 v0.s[0]
。 GNU 汇编器确实允许 v0.4s[0]
,但其他汇编器(Clang/LLVM 内置汇编器和 Microsoft 的 armasm64)只允许前一种语法。
// 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 CPU 上将其添加到 运行。 我找到了这张幻灯片: porting to ARM64
最后显示的是矩阵乘法代码。但它使用循环,我猜想(如果我没看清这一点,请纠正我)如果移植到新的 ARMv8 助记符,我发布的代码会更快。 链接文档还显示了一些 v7 -> v8 更改,例如我将 vmul.32 更改为 fmul 等等。示例中给出的寄存器名称与上面发布的代码中的名称不匹配。由于我并不完全精通任何 ARM asm,所以我不知道这里有什么等价物。 例如。当我构建我的项目时,出现如下错误:
operand 1 must be a SIMD vector register list -- `st1 {d24-d27},[r0]
不过,我不确定这是唯一的问题,所以我想问: 在 aarch64 机器上 运行 的代码需要做哪些更改?
这是该例程的粗略 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
除了链接演示文稿中提到的一般事项外,关于转换的一些不全面的说明:
- 与 AArch32 中的
vld1
相比,您可以使用一条ld1
指令加载最多 64 个字节。这避免了增加 r0/r1/r2 或 x0/x1/x2 指针 的需要
- 我省略了 OS/binary 特定格式
.fnstart
、.fnend
和.type
,如果需要,可以在与原始版本相同的位置重新添加它们 - 对于 AArch64 程序集,
@
不再是注释字符 -
mul_col_f32
的col
参数采用v0.s
形式,与v0.4s
相反。选择特定车道时,在宏中与[0]
后缀连接后,应省略车道数,例如要选择v0.4s
寄存器的第一通道,应写为v0.s[0]
。 GNU 汇编器确实允许v0.4s[0]
,但其他汇编器(Clang/LLVM 内置汇编器和 Microsoft 的 armasm64)只允许前一种语法。