【arm】arm架构32位汇编优化总结

版权声明:本文为博主原创文章,未经博主允许不得转载。若允许转载,请注明来源https://blog.csdn.net/SoaringLee_fighting,否则保留追究法律责任的权利! https://blog.csdn.net/SoaringLee_fighting/article/details/81743505

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

中文手册:
https://developer.arm.com/products/software-development-tools/compilers/arm-compiler-5/docs/dui0529/j/overview-of-arm-compiler/about-the-toolchain-documentation

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  r0r1, #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、汇编优化基本准则
  1. C代码优化
    ** 减少计算量,将重复计算的部分提取出来;
    ** 深入剖析C代码的实现原理,更改结构,把可以合并的代码进行合并,简化计算,减少分支判断。
  2. 汇编代码优化
    ** 精简指令,大部分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!

猜你喜欢

转载自blog.csdn.net/SoaringLee_fighting/article/details/81743505