【arm】arm架构32位汇编优化总结
Date: 2018.8.18
1、参考:
https://blog.csdn.net/SoaringLee_fighting/article/details/80764811
https://blog.csdn.net/SoaringLee_fighting/article/details/81287824
https://blog.csdn.net/SoaringLee_fighting/article/details/81058147
https://blog.csdn.net/SoaringLee_fighting/article/details/80770034
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.den0018a/index.html
2、Arm汇编架构和Reference Manuals
ARM是RISC(精简指令集)处理器,不同于x86指令集(CISC,复杂指令集)。
Arm32位是ARMV7架构,32位的,对应处理器为Cortex-A15;
iphone5以前均是32位的;
需要注意:ARMV7-A和ARMV7-R系列支持neon指令集,ARMv7-M系列不支持neon指令集。
ARMV7架构A和R系列参考手册下载地址:
https://static.docs.arm.com/ddi0406/cd/DDI0406C_d_armv7ar_arm.pdf
Arm64位是ARMV8架构,64位的,对应处理器有Cortex-A53、Cortex-A57、iphone5s的A7、iphone6的A8等。
ARMV8架构参考手册下载地址:
https://developer.arm.com/docs/ddi0487/latest/arm-architecture-reference-manual-armv8-for-armv8-a-architecture-profile
https://static.docs.arm.com/ddi0487/ca/DDI0487C_a_armv8_arm.pdf
所有ARM参考文档地址:
https://developer.arm.com/docs
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0403e.b/index.html
3、Arm32位寄存器
主要分为ARM寄存器和NEON寄存器。
ARM32寄存器包括15个通用寄存器R0~R14和一个程序计数器PC,共16个,均为32位宽。
ARM32位寄存器的调用规则:遵循ATPCS调用规则,详细参见
https://blog.csdn.net/SoaringLee_fighting/article/details/81287824
32位 NEON寄存器:
包括:32个S寄存器,S0~S31,(单字,32bit)
32个D寄存器,D0~D31,(双字,64bit)
16个Q寄存器,Q0~Q15,(四字,128bit)
寄存器的对应关系如下图所示:
使用注意:
1、NEON寄存器将每个寄存器均视为一个向量,该向量又包含1,2,4,8或16个大小和类型均相同的元素。也可以将各个元素当做标量访问。
NEON的这三种寄存器是重叠的,物理地址是一样的。
2、NEON寄存器在使用时,如果用到d8~d15寄存器,需要先入栈保存vpush {d8-d15},使用完之后要出栈vpop {d8-d15}
4、ARM指令寻址方式
ARM指令集的寻址方式与x86指令集大部分相同,但也有其特有的寻址方式,比如寄存器偏移寻址,多寄存器寻址和堆栈寻址。
ARM指令共有9种寻址方式,具体参见:
https://blog.csdn.net/SoaringLee_fighting/article/details/80770034
5、ARM指令特点以及优化技巧
ARM汇编特点1:LDR/STR架构
ARM采用RISC架构,CPU本身不能直接读取内存,而需要先将内存中内容加载入CPU中通用寄存器中才能被CPU处理。
ldr(load register)指令将内存内容加载入通用寄存器。
str(store register)指令将寄存器内容存入内存空间中。
ldr/str组合用来实现 ARM CPU和内存数据交换。
ARM汇编特点2:指令后缀
同一指令经常附带不同后缀,变成不同的指令。经常使用的后缀有:
B(byte)功能不变,操作长度变为8位
H(half word)功能不变,长度变为16位
S(signed)功能不变,操作数变为有符号
如 ldr ldrb ldrh ldrsb ldrsh
S(S标志)功能不变,影响CPSR标志位
W(宽型)
L(长型)
N(窄型)
S(饱和)
Q(舍入取整)
ARM汇编特点3:条件执行
subgt,addle等,只有在上一条指令执行之后相应标志位满足条件之后,当前指令才会执行,通过使用条件执行指令可以减少分支跳转。
ARM汇编特点4:多级流水线技术
ARM7处理器(对应架构armv3或armv4)采用3级流水线的冯·诺伊曼结构;而ARM9(对应架构armv4或armv5)用5级流水线的哈佛结构,ARM11(对应架构armv6)为8级流水线哈佛结构(从arm9开始都采用了哈佛结构)。增加的流水线设计提高了时钟频率和并行处理能力。5级流水线能够将每一个指令处理分配到5个时钟周期内,在每一个时钟周期内同时有5个指令在执行。在常用的芯片生产工艺下,ARM7一般运行在100MHz左右,而ARM9则至少在200MHz以上.ARM11首先推出350M~500MHz时钟频率的内核,目前上升到1GHz时钟频率。
参考:https://blog.csdn.net/SoaringLee_fighting/article/details/81411760
ARM NEON优化技巧总结,参见:
https://blog.csdn.net/SoaringLee_fighting/article/details/81265865
https://blog.csdn.net/SoaringLee_fighting/article/details/81705311
6、ARM和NEON指令集 常用指令汇总
ARM指令集:32位,工作在ARM模式下。
Thumb指令集:16位,工作在Thumb模式下。
NEON指令集:以v开头,基于ARMv7架构的SIMD和向量浮点VFPv3指令集。
ARM算术指令:
add, adc, sub, subs, rsb, mul, udiv等
ARM移位指令:
lsl, lsr, ror,asr等
ARM饱和指令:
ssat, usat,qadd,qsub
ARM逻辑运算指令:
orr,and,orn,eor,
NEON逻辑运算和比较指令:
vand,vorr, vbic, vorn
vbif,bsl,vbit
vmov,vmvn
vceq,vcge,vcgt,vcle,vclt
vtst
NEON移位指令:
vshr,vshl,vqshl,vqrshrun
vsli,vsri
NEON通用算术指令:
vabs,vabd,vneg, vadd,vsub,vqadd,vqsub,vaddl,vaddw,vsubl,vsubw
vaddhn,vsubhn
vhadd,vhsub
vpadd,vpadal
vmax,vmin,vpmax,vpmin
NEON乘法指令:
vmul,vmla,vmls
vext指令:向量提取
vext.8 d2, d0, d1, #3
说明:取d1寄存器中低3位向量作为高位,d0寄存器的高5位向量作为低位,构成目标向量。
asr和lsr的区别:
asr r0, r1, #5 //算术右移,符号位填充左侧空出的位
lsr r0, r1, #5 //逻辑右移,0填充左侧空出的位
vpadd:向量按对加
vceq,vcgt,vcge, vcle,vclt指令:
向量比较,获取向量中每个元素的值,并将其与另一个向量重相应元素或零进行比较。如果条件为真,则将目标向量中的全部元素设置为1,否则设置为0。
vrshr指令:舍入右移 ,可以实现(a+(1<<(b-1)))>>b的操作。
vaddl和vaddw指令:加法长指令、加法宽指令
vqmovun指令:有无符号操作数,无符号结果
vqmovun.s16 d0, q0
说明: 将q0中每个16位有符号向量饱和到d0中每个8位无符号向量。
7、Arm32位加载数据的两种格式
1)、vld1加载:
vld1.8 {d0,d1} , [r1], r2
说明: 将r1地址里面的连续的128bits数据依次赋给d0和d1,然后r1+r2。这里的.8表示以8bit为单位。
2)、
vld1.16 {d0[],d1[]}, [r0:16]
说明:这里d0和d1中的数据相同,将地址r0中取4个16位数据加载到d0和d1中。
8、Arm32位汇编编写demo
https://blog.csdn.net/SoaringLee_fighting/article/details/81150083
ARM汇编格式主要有两种,arm asm汇编格式和gnu asm汇编格式。
gnu asm汇编格式:
.arm
.text
.align 4
.global name
.type %function
name:
FUNCTION STATEMENT @注释行
/* 多行注释 */
//单行注释,用于.S汇编文件
bx lr
arm asm汇编格式:
EXPORT |name|
ARM
AREA ||.text||, CODE, READONLY,ALIGN=2
|name| PROC ;注释
ENDP
END
9、注意事项
1)标签名称不能以数字开始,但是可以使用纯数字的局部标签。
2)ld1连续存储数据时,所用的寄存器必须是连续的。
3)Arm32位下数据在不同寄存器之间转换:
从r寄存器到d寄存器:
vmov d0, r0, r1
vmov.u32 d0[0], r1
从d寄存器到r寄存器:
vmov r0, r1, d0
vmov.u32 r1, d0[0]
从标量寄存器d[x]到矢量寄存器d:
vdup.16 d1, d6[0]
vdup.16 d1, r12
4)Arm32位下替代判断的命令:
vceq,vbsl,vbit, vbif
5)Arm32下取数据地址问题
arm下默认地址r0加1,是加一个字节,如果r0对应的数据是int类型的,则取idx位置的数据则为:r0+idx*4
10、汇编优化基本准则
- C代码优化
** 减少计算量,将重复计算的部分提取出来;
** 深入剖析C代码的实现原理,更改结构,把可以合并的代码进行合并,简化计算,减少分支判断。 - 汇编代码优化
** 精简指令,大部分arm指令都是单周期指令,尽量使用较少的指令编写代码;
** 减少寄存器之间的依赖,充分利用多级流水线,使指令并行执行;
** 对于乘法指令,指令周期比较长,尽量不要立即使用指令计算结果,否则会等待耗时;
** 尽量将数据都存放在neon寄存器中;
** 尽量减少存取数据的次数。
11、ARM程序调试
下面是最基本的方法:
汇编文件中添加如下宏代码:
.macro print_m in1=r0, in2=d0
push {r0-r3, lr}
vstl.u64 {\in2\()}, [\in1\()]
mov r0, \in1
bl cprintf
pop {r0-r3, pc}
.endm
C文件中添加cprintf实现代码:
void cprintf(unsigned char *srcu8)
{
int i=0;
char *srcs8 = (char *)srcu8;
for(i=0; i < 16; i++){
printf("%d ", srcu8[i])
}
for(i=0; i < 16; i++){
printf("%d ", srcs8[i])
}
printf("\n");
}
关于arm寄存器的打印调试方法,参见:
https://blog.csdn.net/SoaringLee_fighting/article/details/80834098
除了上述基本方法以外,可以借助RVDS或GDB软件进行调试。
注意事项:
1、RVDS只能用于ARM32位调试,ARMV8架构不支持RVDS。
2、采用GDB调试的前提是ARM开发板上已经安装好了GDB,采用GDB进行调试是很方便的。
THE END!
上一篇: winform 杂记