【arm】arm32位和arm64位架构、寄存器和指令差异分析总结

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

Date: 2018.9.21


1、参考

https://blog.csdn.net/SoaringLee_fighting/article/details/82155608
https://blog.csdn.net/SoaringLee_fighting/article/details/81743505
https://blog.csdn.net/SoaringLee_fighting/article/details/81906495
https://blog.csdn.net/SoaringLee_fighting/article/details/82530435

2、前言

  最近三个月的时间,都在进行解码库的arm架构汇编优化,包括arm32位汇编优化和arm64位汇编优化。在arm32位入门之后,只要掌握了两种架构的寄存器和指令集差异之后,就可以很快上手编写arm64位汇编代码了。下面就arm32位和arm64位架构、寄存器和指令差异进行分析总结。

3、架构差异

  ARM是RISC(精简指令集)处理器,不同于x86指令集(CISC,复杂指令集)。
  Arm32位是ARMV7架构,32位的,对应处理器为Cortex-A15等; iphone5以前均是32位的;
需要注意:ARMV7-A和ARMV7-R系列支持neon指令集,ARMv7-M系列不支持neon指令集。
  ARM64位采用ARMv8架构,64位操作长度,对应处理器有Cortex-A53、Cortex-A57、Cortex-A73、iphones的A7和A8等。

4、寄存器差异

ARM32位通用寄存器和ARM64位通用寄存器差异详见:ARM寄存器及其说明

ARM32位neon寄存器和ARM64位neon寄存器差异:
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}。

64位下NEON寄存器:
包括:

  • 32个B寄存器(B0~B31),8bit
  • 32个H寄存器(H0~H31),半字 16bit
  • 32个S寄存器(S0~S31),单子 32bit
  • 32个D寄存器(D0~D31),双字 64bit
  • 32个Q寄存器(V0~V31),四字 128bit

不同位数下寄存器之间的关系如下图所示:
这里写图片描述

其中S0是D0的低半部分,D0是V0的低半部分 。

注意:
64位下NEON寄存器与32位下NEON寄存器之间的关系不同!
neon寄存器 v0~v31使用说明:
v0~v7:用于参数传递和返回值,子程序不需要保存;
v8~v15:子程序调用时必须入栈保存(低64位);
v16~v31:子程序使用时不需要保存。
具体可参考:
http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf 5.1.2 SIMD and Floating-Point Registers

5、A64指令集特有的指令及其用法(指令差异)

AARCH64是全新32位固定长度指令集,支持64位操作数的新指令,大多数指令可以具有32位或64位参数。

  • 32位neon指令都是以V开头,而64位neon指令没有V;
  • 32位寄存器需要保存的是r4~r11,q4~q7,而64位寄存器需要保存的是x19~x29
    ,v8~v15;
  • 64位下NEON寄存器与32位下NEON寄存器之间的关系不同;
  • 向64位或者更低位的矢量寄存器中写数据时,会将高位置零;
  • 在AArch64中,没有通用寄存器的SIMD和饱和算法指令。只有neon寄存器才有SIMD或饱和算法指令;
  • ARM64下没有swap指令和条件执行指令。
  • 关于指令中立即数取值范围的说明:
    不同指令中的#或者#<具有不同的取值范围,这个取决于所使用的指令,比如:
mov		<wd>, #<imm>  //该指令中立即数范围为-65536~65535。
cmp	   <wn>, #<imm> //该指令中#<imm>为无符号立即数,取值范围为0~4095(12 bit)。

特别说明:大部分ARM指令中的立即数不能是负数,需要注意不同指令的取值范围。

  1. shl和ushr指令

	shl  <V>.<d>, <V>.<n>, #<shift>
	ushr  <V>.<d>, <V>.<n>, #<shift>
	ushr  d2, d2,  #8

使用注意事项:这两条指令只能操作64位数据,即只能对D寄存器进行处理。
ushr最多只能进行64位数据的右移,并且右移时会影响V2寄存器的高64位数据(清零),因此高64位数据需要在右移前保存,否则相关数据会被修改。

  2. INS指令
用法与MOV指令基本一样,可以实现neon标量与neon标量之间的传送,以及ARM寄存器与neon标量之间的传送。

INS   <Vd>.<Ts>[index1], <Vn>.<Ts>[index2]
INS   <Vd>.<Ts>[index1], Rn

  3. SUQADD、USQADD指令
既有标量用法,也有矢量用法。

SUQADD <V><d>, <V><d>     // signed saturating accumulate of unsigned value
SUQADD <Vd>.<T>, <Vn>.<T>

USQADD <V><d>, <V><d>    // unsigned saturating accumulate of signed value
USQADD <Vd>.<T>, <Vn>.<T>

  4. RBIT、REV指令

 RBIT <Wd>, <Wn> //reverse bits
 REV <Wd>, <Wn>  //reverse bytes

 5. ADDV,SADDLV,SMAXV,SMINV (Vector Reduce(across lanes))
后缀带V指令:

ADDV <V><d>, <Vn><T>    // Integer sum element to scalar(vector)
SADDLV <V><d>, <Vn><T>  // Signed Interger sum elements to long scalar(vector)
SMAXV <V><d>, <Vn><T>   // Signed Interger maximum elements to scalar(vector)
SMINV <V><d>, <Vn><T>   // Signed Interger minimum elements to scalar(vector)

eg.:
addv B0, v1.8B			// 将v1寄存器中的低64位中8个8位数据相加求和后,赋给v0的最低8位。

用法说明:
vector reduce instrunctions perform arithmetic operations horizontally, that is across all lanes of the input vector. they deliver a single scalar result.

更多详细解释可以参考:https://static.docs.arm.com/ddi0487/a/DDI0487A_j_armv8_arm.pdf
这里写图片描述
 6. sxtw使用注意事项
负数在使用时必须进行符号扩展!
比如:

sxtw   x4, w4

  7. w寄存器到v寄存器
直接使用dup指令

dup		v0.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

  9. 指令结尾带“2”的寄存器高64位操作:

 smull2 v0.4S,	v1.4H, v2.4H //将v1和v2高64位中每一个16位相乘,并将结果放在v0的每个32位中。
 sqxtn2  v5.8H, v4.4S  //将v4中的每个32位元素,饱和缩进到v5高64位的每个16位中。
 sxtl2  v16.4S, v17.4H //将v17高64位中的每个16位元素,扩展到v16的每个32位元素中。

  10. ADDP,SMAXP,SMINP,UMAXP,UMINP
后缀带P指令:

addp  v1.8B, v2.8B, v3.8B   // add pairwise

用法说明:
the SIMD pairwise arthmetic instructions perform operations on pairs of adjacent element and deliver a vector result.

  11. 替代arm32下条件执行指令的arm64位指令:

arm32位:

   bgt,addgt,suble等

arm64位:

b.gt,csinc, csel, cset, csetm,  csnev

说明: 在ARM64下没有条件执行指令。

6、AArch32与AArch64的区别
6.1 入栈和出栈:

arm64位(aarch64架构):
(1)arm寄存器入栈和出栈:

入栈:
	sub  sp, sp, #0x10
	stp  x8, x9, [sp] // 寄存器成对入栈
出栈:
	ldp  x8,  x9, [sp] 
	add  sp,  sp, #0x10 //寄存器成对出栈

原则:1、堆栈入栈和出栈后,SP指针应该保持不变。2、LIFO。 3、特别注意是,从SP位置存取数据都是从低地址开始的。
(2)neon寄存器入栈和出栈:
ARM64位三种入栈出栈方法:
方法一:

stp		d8,d9, 	[sp,	#-64]!
stp		d10,d11,[sp,	#16]!
stp		d12,d13,[sp,	#16]!
stp		d14,d15,[sp,	#16]!

ldp		d14,d15,[sp],	#-16
ldp		d12,d13,[sp],	#-16
ldp		d10,d11,[sp],	#-16
ldp		d8,d9,[sp],		#64		//恢复sp位置

方法二:

stp		d8,d9, 	[sp,	#-16]!
stp		d10,d11,[sp,	#-16]!
stp		d12,d13,[sp,	#-16]!
stp		d14,d15,[sp,	#-16]!

ldp		d14,d15,[sp],	#16
ldp		d12,d13,[sp],	#16
ldp		d10,d11,[sp],	#16
ldp		d8,d9,[sp],		#16		//恢复sp位置

方法三:

.macro push_v_regs
    stp    d8,  d9,  [sp, #-16]!
    stp    d10, d11, [sp, #-16]!
    stp    d12, d13, [sp, #-16]!
    stp    d14, d15, [sp, #-16]!
.endm
.macro pop_v_regs
    ldp    d14, d15, [sp], #16
    ldp    d12, d13, [sp], #16
    ldp    d10, d11, [sp], #16
    ldp    d8,  d9,  [sp], #16
.endm

arm32位:

push	{r4-r11,  lr}
vpush	{d8-d15}

vpop	{d8-d15}
pop		{r4-r11,  pc}
6.2 4x4矩阵转置:

arm64位(aarch64架构):

.macro transpose4x4B  r0,r1,r2,r3,t4,t5,t6,t7
trn1	\t4\().8B,	\r0\().8B,	\r1\().8B
trn2	\t5\().8B,	\r0\().8B,	\r1\().8B
trn1	\t6\().8B,	\r2\().8B,	\r3\().8B
trn2	\t7\().8B,	\r2\().8B,	\r3\().8B

trn1	\r0\().8B,	\t4\().8B,	\t6\().8B
trn1	\r1\().8B,	\t5\().8B,	\t7\().8B
trn2	\r2\().8B,	\t4\().8B,	\t6\().8B
trn3	\r3\().8B,	\t5\().8B,	\t7\().8B
.endm

arm32位:

vtrn.16 q0,	q1
vtrn.8	d0,	d1
vtrn.8	d2,	d3
6.3 Difference between AArch64 and AArch32

这里写图片描述
这里写图片描述
来源参考:https://www.nxp.com/docs/en/application-note/AN12212.pdf


THE END!

猜你喜欢

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