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

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等,苹果手机从iphone 5s开始使用64位的处理器。

4、寄存器差异
4.1 ARM通用寄存器

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

4.2 NEON寄存器

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寄存器之间的关系不同,32位下d寄存器和q寄存器是重叠映射的,而64下的d寄存器和v寄存器是一一对应的,具体详见4.1;
  • 向64位或者更低位的矢量寄存器中写数据时,会将高位置零;
  • 在AArch64中,没有通用寄存器的SIMD和饱和算法指令。只有neon寄存器才有SIMD或饱和算法指令;
  • ARM64下没有swap指令和条件执行指令。
  • 关于指令中立即数取值范围的说明:
    不同指令中的#<imm>或者#<const>具有不同的取值范围,这个取决于所使用的指令,比如:
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位。
addv H2, v1.8B          // 将v1寄存器中的低64位中8个字节依次相加求和,并将结果赋给v1第2个16位标量中。

用法说明:
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

6.4 加载数据和存储数据差异

ARM32位存取数据的格式:
vld1加载和vst1存储:

vld1.8 {d0,d1} , [r1], r2  // 将r1地址里面的连续的128bits数据依次赋给d0和d1,然后r1+r2。这里的.8表示以8bit为单位。 
vld1.16 {d0[],d1[]}, [r0:16] //这里d0和d1中的数据相同,将地址r0中取4个16位数据加载到d0和d1中。
vst1.8  {d0,d1}, [r2], r3  //将d0和d1中32个bytes数据存储到r2地址处。

ARM64位存取数据的格式:

ld1 {v20.8H, v21.8H}, [x1]  // 从x1指向的存储单元位置一次性加载128*2位数据到v20和v21中
ld1 {v1.8B},    [x1],   x2  // 从x1指向的存储单元位置加载64位数据到v1的低64位中,然后x1=x1+x2
ld1 {v18.S}[0], [x0],   x1  // 将x0地址里面的数据取32位加载到v18的最低32位,然后x0=x0+x1
ld1r {v30.8H},  [x1]        // 从x1地址中以16位为单位取128位加载到v30中。

st1 {v30.8H},   [x1],   #16 // 将 寄存器v30中128位数据存储到x1地址处,然后x1=x1+16
st1 {v0.S}[0],  [x0],   x2  // 将 寄存器v0的低32位数据存储到x0地址处吗,然后x0=x0+x2

6.5 补充知识
  1. 关于调用规则和SP
    ARM32位的调用规则遵循ATPCS(The ARM-THUMB Procedure Call Standarfd)调用规则,从2007年开始ARM公司正式推出AAPCS(Procedure Call Standard for the ARM Architecture)标准,是ATPCS的改进版,目前两者都是可用的标准。
    AAPCS调用规则规定,栈任何时候必须4字节对齐,在调用入口需8字节对齐。
    ARM64位下,SP在公共接口或访问内存时均必须保证16字节对齐,这是硬件要求的。
  2. 指令编码长度
    不管是aarch32还是aarch64,指令在arm指令集模式下,指令固定编码长度为32bit。
  3. 获取标签地址的方法
    arm32位下可以使用adr指令、adrl(伪指令)和ldr指令获取标签地址,需要注意使用adr、adrl指令的标签要在同一代码节中,ldr可以在不同节中。
    arm64位下,可以使用adr和adrp采用相对寻址的方式来实现,64位下不支持伪指令adrl。
  4. EL0没有权限进行AArch64和AArch32状态切换。因此A64指令无法切换到A32和T16指令。因此ARM64位指令无法在ARM32位设备上运行。
    疑问: Android 64位指令可以在32位设备上运行?
  5. 关于PC
    ARM32位下:PC=当前指令地址+8,具体跟arm处理器多级流水线机制相关,不可用作通用寄存器。
    ARM64位下:PC不能直接访问,但可以通过伪指令间接使用。需要注意的是:与32位不同的是,64位下当通过指令读取PC相对地址时,其值即为当前指令的地址。
    (1). 64位可读取PC值的情况有:
    计算相对地址,如adr,adrp,文字池加载以及直接分支;
    子程序返回地址,比如bl,blr
    (2). 可修改pc的方式为:
    使用控制流指令,如条件跳转、无条件跳转、异常生成和异常返回指令。

<font color=red>** THE END! ** </font>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,378评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,356评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,702评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,259评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,263评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,036评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,349评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,979评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,469评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,938评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,059评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,703评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,257评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,262评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,501评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,792评论 2 345

推荐阅读更多精彩内容