欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2022-06-08 22:17:27
...

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)
寄存器的对应关系如下图所示:
【arm】arm架构32位汇编优化总结

使用注意:
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!