1、参考
https://blog.csdn.net/SoaringLee_fighting/article/details/81906495
https://blog.csdn.net/SoaringLee_fighting/article/details/82155608
https://blog.csdn.net/u011514906/article/details/38142177
https://blog.csdn.net/listener51/article/details/82530464
2、前言
本文是arm架构64位(AArch64执行状态) neon优化的总结文档 主要包括arm架构64位优化的基础知识 特殊用法 打印调试和常用指令使用注意事项以及资料来源等相关知识。前文已有arm架构32位汇编优化总结对arm架构32位neon优化进行了全面总结 并且讲述了arm汇编语法,下面主要以gnu asm汇编语法为例讲述。
下图为arm架构汇编优化总结的思维导图
3、arm架构64位优化基础知识
【arm】arm架构64位入门基础 架构分析、寄存器、调用规则、指令集以及参考手册
该博客已经分析了arm架构64位汇编优化的入门基础知识,主要包括架构分析,寄存器,调用规则,指令集和程序打印调试相关知识,可以作为入门arm64位汇编优化的基础知识。
4、ARMv8/AArch64 neon指令格式
In the AArch64 execution state, the syntax of NEON instruction has changed. It can be described as follows:
{}{} Vd., Vn., Vm.
Where:
prefix - prefix, such as using S/U/F/P to represent signed/unsigned/float/bool data type.
op – operation, such as ADD, AND etc.
suffix - suffix
P: “pairwise” operations, such as ADDP LDP STP.
V: the new reduction (across-all-lanes) operations, such as ADDV SMAXV FMAXV.
2 new widening/narrowing “second part” instructions, such as ADDHN2, SADDL2 SMULL2.
T - data type, 8B/16B/4H/8H/2S/4S/2D. B represents byte (8-bit). H represents half-word (16-bit). S represents word (32-bit). D represents a double-word (64-bit).
For example:
UADDLP V0.8H, V0.16B
FADD V0.4S, V0.4S, V0.4S
参考自
https://community.arm.com/android-community/b/android/posts/arm-neon-programming-quick-reference
5、ARM相关编译参数
嵌入式设备(即arm架构的板子)在编译时 最好加上 -fsigned-char 因为嵌入式设备默认类型为unsigned char类型 非char 类型。此外在编译arm汇编优化代码时 编译选项需要加上-c 。-c都表示编译或汇编源文件,但是不进行链接。
ARM相关或者硬件相关编译参数一般以-m开头,常用ARM平台编译选项包括:
-mcpu cortex-a7
-mabi atpcs
-march armv7
-mtune cortex-a53
-mfpu neon, neon-vfpv4
-mfloat-api soft, softfp, hard
更多详细内容可以参考:https://gcc.gnu.org/onlinedocs/gcc-5.2.0/gcc.pdf 3.17.1小节AArch64 option和3.17.4小节 ARM options.
6、查看状态标记位NZCV的方法
mrs x15, nzcv
mov w0, w15
blprint
7、A64指令集特有的指令及其用法
1. shl和ushr指令
shl ., ., #
ushr ., ., #
ushr d2 d2, #8
使用注意事项 这两条指令只能操作64位数据 即只能对D寄存器进行处理。
ushr最多只能进行64位数据的右移 并且右移时会影响V2寄存器的高64位数据(清零) 因此高64位数据需要在右移前保存 否则相关数据会被修改。
2. INS指令
用法与MOV指令基本一样,可以实现neon标量与neon标量之间的传送,以及ARM寄存器与neon标量之间的传送。
INS .[index1], .[index2]
INS .[index1], Rn
3. SUQADD、USQADD指令
既有标量用法,也有矢量用法。
SUQADD , // signed saturating accumulate of unsigned value
SUQADD ., .
USQADD , // unsigned saturating accumulate of signed value
USQADD ., .
4.RBIT、REV指令
RBIT , //reverse bits
REV , //reverse bytes
5.
ADDV,SADDLV,SMAXV,SMINV (Vector Reduce(across lanes))
ADDV , // Integer sum element to scalar(vector)
SADDLV , // Signed Interger sum elements to long scalar(vector)
SMAXV , // Signed Interger maximum elements to scalar(vector)
SMINV , // Signed Interger minimum elements to scalar(vector)
eg.:
addv B0, v1.8B// 将v1寄存器中的低64位中8个8位数据相加求和后 赋给v0的最低8位。
更多详细解释可以参考 https://static.docs.arm.com/ddi0487/a/DDI0487A_j_armv8_arm.pdf
这里写图片描述
6.sxtw使用注意事项
负数在使用时必须进行符号扩展
比如
sxtw x4, w4
7.w寄存器到v寄存器
直接使用dup指令
dupv0.8B, w2
8.常用指令对应关系(arm32---- arm64)
vmovl------ uxtl/sxtl
vqmovn----- sqxtn
vqmovun----- sqxtun
vqrshrun---- sqrshrun
vceq------- cmeq
vcge------- cmge
vadd------ add
vsub------ sub
vaddl----- saddl,uaddl
vaddw----- saddw,uaddw,sw2addw2,uadd
vmull----- smull,smull2,umull,umull2
vmax,vmin----- smax,umax,smin,umin
vmlal-------- smlal,smlal2,umlal,umlal2
vrshl-------- urshl,srshl
vtrn--------- trn1,trn2
vstm/vstr---- stp/str
vld1.32 {d0[]}, [r0], r2----- ld1r {v0.S}[0], [x0], x2
addgt,addle,subgt,suble----- csel,csetm,cset,csinc,csinv
更多可参考:
https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf
8、资料文档查阅
在进行arm64位汇编语言编写之前 建议首先阅读学习arm官方英文手册(https://static.docs.arm.com/ddi0487/ca/DDI0487C_a_armv8_arm.pdf) 重点阅读C7 AArch64 neon指令部分以及C3 ARM指令部分 在了解了基本指令和arm64位汇编格式之后就可以尝试编写了。
对于已有arm32位代码的情况下 从arm32位代码迁移到arm64时 可以参考(https://www.element14.com/community/servlet/JiveServlet/previewBody/41836-102-1-229511/ARM.Reference_Manual.pdf) 中 5.7.23小节的指令对照表。
对于代码迁移方法 可以参考我的博客 Some ways of Migrating code from ARM32 to AArch64。
对于快速查找指令 可以参考指令速查卡
https://courses.cs.washington.edu/courses/cse469/18wi/Materials/arm64.pdf
9、优化经验总结(满满的干货)
关于参数入栈和寄存器入栈
建议将入栈的参数取出之后 再对ARM寄存器或者NEON寄存器进行入栈。
尽量去除数据依赖 使指令并行
不要将当前指令的目的寄存器作为下一条指令的源寄存器 尤其对于vmul指令 vmla指令。 不要将当前指令的目的寄存器作为下一条指令的源寄存器 尤其对于vmul指令 vmla指令。
尽量减少分支跳转
可以采用条件执行指令或逻辑运算指令替代分支跳转 比如addgt,suble,vceq,vcge,vbit,vbsl等。
关注指令周期延迟
对于乘法指令 指令周期比较长 尽量不要立即使用指令计算结果 否则会等待耗时。
数据运算尽量在neon寄存器中 避免在arm寄存器和neon寄存器之间的运算。
尽量减少存取数据的次数。
尽量使用不需要保存的寄存器 寄存器出入栈很耗时。
对于宽度为4的倍数的情况下 尽量在宽度方向上处理 这样可以提高cache命中率。
使用尽量少的指令来编写代码 因为arm指令是精简指令 大部分指令都是单周期指令。
如果寄存器够用的话 尽量将一行的数据处理拆成两行或是四行来并行处理 尽量避免大数据之间的运算 可以将大数据的运算拆成小数据的运算。
减少循环判断和条件比较
在数据处理过程中 在循环判断或条件判断较多时 可以适当展开分支 可以在一定程度上提升性能。
THE END!
点赞 评论本文链接: http://archwaystl.immuno-online.com/view-697113.html