ARM寄存器组详解

ARM寄存器组详解

引言

ARM架构是当今最广泛使用的处理器架构之一,从嵌入式设备到高性能服务器,ARM处理器几乎无处不在。理解ARM的寄存器组对于任何希望在底层编程或优化ARM系统的人来说都是至关重要的。

ARM寄存器组概述

寄存器是处理器内部的高速存储单元,比内存快数百倍。ARM处理器的性能很大程度上依赖于其精心设计的寄存器组。ARM架构的寄存器组随着不同版本(如ARMv7、ARMv8)略有不同,但核心概念保持一致。以下我们主要以ARMv8-A架构为例进行讲解。

从理论上讲,寄存器访问速度可以通过以下公式量化:

T 访问 = 1 f C P U T_{访问} = \frac{1}{f_{CPU}} T访问=fCPU1

其中 f C P U f_{CPU} fCPU是CPU的时钟频率。相比之下,内存访问延迟通常是:

T 内存 = n f C P U T_{内存} = \frac{n}{f_{CPU}} T内存=fCPUn

其中 n n n通常在数十到数百个周期之间,这就解释了为什么寄存器访问比内存访问快几个数量级。

核心寄存器

通用寄存器

在ARM架构中,通用寄存器是最基础的寄存器类型。在32位ARM架构(如ARMv7)中,有16个主要的32位寄存器,标记为R0至R15。在64位ARM架构(ARMv8)中,这些扩展为31个64位通用寄存器,标记为X0至X30,同时每个64位寄存器的低32位可以作为W0至W30单独访问。

寄存器使用遵循以下数学公式表达的地址计算:

对于32位模式:
A d d r e s s = B a s e _ R e g i s t e r + O f f s e t Address = Base\_Register + Offset Address=Base_Register+Offset

其中 B a s e _ R e g i s t e r Base\_Register Base_Register通常是R0-R14中的一个, O f f s e t Offset Offset可以是立即数或其他寄存器的值。

对于64位模式:
A d d r e s s = X _ R e g i s t e r + ( O f f s e t × S c a l e ) + E x t e n d ( I n d e x _ R e g i s t e r ) × S c a l e Address = X\_Register + (Offset \times Scale) + Extend(Index\_Register) \times Scale Address=X_Register+(Offset×Scale)+Extend(Index_Register)×Scale

其中 X _ R e g i s t e r X\_Register X_Register通常是X0-X30中的一个, O f f s e t Offset Offset可以是立即数, I n d e x _ R e g i s t e r Index\_Register Index_Register是另一个寄存器, E x t e n d Extend Extend是符号或零扩展函数, S c a l e Scale Scale是缩放因子(可以是1、2、4或8)。

在ARMv8.3及以上版本中,引入了指针认证机制(PAC),通过以下函数保护指针:

P A C ( p t r , k e y , m o d i f i e r ) = ( p t r & ∼ m a s k ) ∣ ( h a s h ( p t r , k e y , m o d i f i e r ) & m a s k ) PAC(ptr, key, modifier) = (ptr \& \sim mask) | (hash(ptr, key, modifier) \& mask) PAC(ptr,key,modifier)=(ptr&mask)(hash(ptr,key,modifier)&mask)

其中 p t r ptr ptr是原始指针, k e y key key是密钥, m o d i f i e r modifier modifier是上下文信息, m a s k mask mask定义了用于存储认证码的位。

特殊用途寄存器

某些寄存器具有特殊用途,例如:

程序计数器(PC):在ARMv7中是R15,指向当前执行的指令地址。在ARMv8中,PC不再是通用寄存器的一部分,而是单独的寄存器。

在分支预测中,ARM使用动态分支预测器预测PC的下一个值,可以用马尔可夫模型表示:

P ( P C t + 1 = t a r g e t ∣ P C t = c u r r e n t ) = f ( h i s t o r y ) P(PC_{t+1} = target | PC_t = current) = f(history) P(PCt+1=targetPCt=current)=f(history)

其中 f ( h i s t o r y ) f(history) f(history)是基于历史执行路径的预测函数。

链接寄存器(LR):在ARMv7中是R14,在ARMv8中是X30,存储子程序返回地址。当调用函数时,返回地址自动存储在LR中,函数返回时使用。数学上表示为:

L R = P C + 4 LR = PC + 4 LR=PC+4 (对于ARM指令集)
L R = P C + 2 LR = PC + 2 LR=PC+2 (对于Thumb指令集)

在递归调用中,LR需要保存在栈上,调用深度与栈使用量的关系为:

S t a c k _ U s a g e = ∑ i = 1 d e p t h f r a m e _ s i z e i Stack\_Usage = \sum_{i=1}^{depth} frame\_size_i Stack_Usage=i=1depthframe_sizei

其中 f r a m e _ s i z e i frame\_size_i frame_sizei是第 i i i层调用的栈帧大小。

栈指针(SP):在ARMv7中是R13,在ARMv8中是单独的SP寄存器,指向当前栈顶。栈操作可以表示为:

入栈操作: S P = S P − s i z e SP = SP - size SP=SPsize
出栈操作: S P = S P + s i z e SP = SP + size SP=SP+size

其中 s i z e size size是操作的总字节数,必须在AArch64中保持16字节对齐,可表示为:

S P m o d    16 = 0 SP \mod 16 = 0 SPmod16=0

ARMv8.1引入了BTI(分支目标识别)机制,通过特殊指令保护间接分支目标,防止ROP攻击:

V a l i d _ T a r g e t = ( I n s t r u c t i o n [ 31 : 0 ] = = B T I _ E n c o d i n g ) Valid\_Target = (Instruction[31:0] == BTI\_Encoding) Valid_Target=(Instruction[31:0]==BTI_Encoding)

程序状态寄存器

ARM架构使用程序状态寄存器来存储处理器状态信息,包括条件标志、中断禁用标志、处理器模式等。

当前程序状态寄存器(CPSR)

CPSR包含处理器的当前状态,其位域分配如下:

C P S R = { N , Z , C , V , Q , J , G E [ 3 : 0 ] , I T [ 7 : 0 ] , E , A , I , F , T , M [ 4 : 0 ] } CPSR = \{N, Z, C, V, Q, J, GE[3:0], IT[7:0], E, A, I, F, T, M[4:0]\} CPSR={N,Z,C,V,Q,J,GE[3:0],IT[7:0],E,A,I,F,T,M[4:0]}

其中:

  • N N N:负数标志,当结果为负数时置1
  • Z Z Z:零标志,当结果为零时置1
  • C C C:进位标志,当无符号运算产生进位时置1
  • V V V:溢出标志,当有符号运算结果溢出时置1
  • Q Q Q:饱和标志,指示是否发生了算术饱和
  • G E [ 3 : 0 ] GE[3:0] GE[3:0]:大于或等于标志,用于SIMD指令
  • I T [ 7 : 0 ] IT[7:0] IT[7:0]:If-Then状态位,用于条件执行
  • E E E:内存访问的大小端模式
  • A A A:对齐检查使能位
  • I I I:IRQ中断禁用标志
  • F F F:FIQ中断禁用标志
  • T T T:Thumb状态标志
  • M [ 4 : 0 ] M[4:0] M[4:0]:处理器模式位

条件执行的完整数学表达:

E Q : Z = 1 EQ: Z = 1 EQ:Z=1
N E : Z = 0 NE: Z = 0 NE:Z=0
C S / H S : C = 1 CS/HS: C = 1 CS/HS:C=1
C C / L O : C = 0 CC/LO: C = 0 CC/LO:C=0
M I : N = 1 MI: N = 1 MI:N=1
P L : N = 0 PL: N = 0 PL:N=0
V S : V = 1 VS: V = 1 VS:V=1
V C : V = 0 VC: V = 0 VC:V=0
H I : C = 1 ∧ Z = 0 HI: C = 1 \land Z = 0 HI:C=1Z=0
L S : C = 0 ∨ Z = 1 LS: C = 0 \lor Z = 1 LS:C=0Z=1
G E : N = V GE: N = V GE:N=V
L T : N ≠ V LT: N \neq V LT:N=V
G T : Z = 0 ∧ N = V GT: Z = 0 \land N = V GT:Z=0N=V
L E : Z = 1 ∨ N ≠ V LE: Z = 1 \lor N \neq V LE:Z=1N=V
A L : 不考虑条件标志(永远执行) AL: 不考虑条件标志(永远执行) AL:不考虑条件标志(永远执行)

在ARMv8的AArch64状态中,PSTATE替代了CPSR,引入了额外的位如UAO(用户访问覆盖)、PAN(特权访问从不)、SSBS(推测性存储旁路安全位)等,用于增强安全性和处理器功能。PSTATE的转换状态矩阵可表示为:

P ( P S T A T E t + 1 ∣ P S T A T E t , I n s t r u c t i o n t ) = { 1 , 如果 I n s t r u c t i o n t 导致 P S T A T E t 变为 P S T A T E t + 1 0 , 其他情况 P(PSTATE_{t+1} | PSTATE_t, Instruction_t) = \begin{cases} 1, & \text{如果$Instruction_t$导致$PSTATE_t$变为$PSTATE_{t+1}$} \\ 0, & \text{其他情况} \end{cases} P(PSTATEt+1PSTATEt,Instructiont)={1,0,如果Instructiont导致PSTATEt变为PSTATEt+1其他情况

保存的程序状态寄存器(SPSR)

当处理器模式改变时(如进入异常处理),CPSR的值会被保存到SPSR中,以便在返回时恢复。在ARMv8中,每个异常级别(EL0-EL3)都有自己的SPSR_ELx。

异常返回时的状态恢复可以表示为:

P S T A T E = S P S R _ E L x PSTATE = SPSR\_ELx PSTATE=SPSR_ELx

同时进行异常返回的指令权限检查:

A l l o w e d _ R e t u r n = ( E L _ t a r g e t ≤ E L _ c u r r e n t ) ∧ ( E L _ t a r g e t ≥ 1 ∨ ( E L _ t a r g e t = 0 ∧ ! i s _ s e c u r e _ s t a t e ) ) Allowed\_Return = (EL\_target \leq EL\_current) \land (EL\_target \geq 1 \lor (EL\_target = 0 \land !is\_secure\_state)) Allowed_Return=(EL_targetEL_current)(EL_target1(EL_target=0!is_secure_state))

特殊架构寄存器

ARMv8引入了许多特殊的架构寄存器,用于控制和配置处理器功能。以下是一些重要的特殊寄存器:

系统控制寄存器(SCTLR_ELx)

系统控制寄存器控制内存系统操作、缓存策略等。其状态可以表示为:

M M U _ E n a b l e d = S C T L R _ E L x . M MMU\_Enabled = SCTLR\_ELx.M MMU_Enabled=SCTLR_ELx.M
A l i g n m e n t _ C h e c k = S C T L R _ E L x . A Alignment\_Check = SCTLR\_ELx.A Alignment_Check=SCTLR_ELx.A
D a t a _ C a c h e _ E n a b l e d = S C T L R _ E L x . C Data\_Cache\_Enabled = SCTLR\_ELx.C Data_Cache_Enabled=SCTLR_ELx.C
S t a c k _ A l i g n m e n t _ C h e c k = S C T L R _ E L x . S A Stack\_Alignment\_Check = SCTLR\_ELx.SA Stack_Alignment_Check=SCTLR_ELx.SA

异常链接寄存器(ELR_ELx)

存储异常时的返回地址,替代AArch32中的LR寄存器功能,用于异常处理。

E L R _ E L x = P C e x c e p t i o n + o f f s e t ELR\_ELx = PC_{exception} + offset ELR_ELx=PCexception+offset

其中 o f f s e t offset offset取决于异常类型。

异常合成寄存器(ESR_ELx)

记录异常原因和上下文信息:

E S R _ E L x = { E C [ 5 : 0 ] , I L , I S S [ 24 : 0 ] } ESR\_ELx = \{EC[5:0], IL, ISS[24:0]\} ESR_ELx={EC[5:0],IL,ISS[24:0]}

其中 E C EC EC是异常类, I L IL IL是指令长度, I S S ISS ISS是特定于异常的症状信息。

浮点寄存器

VFP寄存器

ARM浮点处理单元(VFP)提供了专门的浮点寄存器。在ARMv7架构中,有32个32位浮点寄存器S0-S31,它们可以组合成16个64位寄存器D0-D15。在ARMv8架构中,有32个128位寄存器V0-V31,可以作为浮点寄存器或SIMD寄存器使用。

浮点运算遵循IEEE 754标准,基本操作可以表示为:

加法: S d = S n + S m S_d = S_n + S_m Sd=Sn+Sm
乘法: S d = S n × S m S_d = S_n \times S_m Sd=Sn×Sm
乘加: S d = S n × S m + S a S_d = S_n \times S_m + S_a Sd=Sn×Sm+Sa

浮点表示遵循IEEE-754标准,对于单精度浮点数:

V a l u e = ( − 1 ) s × 2 e − 127 × ( 1. f ) Value = (-1)^s \times 2^{e-127} \times (1.f) Value=(1)s×2e127×(1.f)

其中 s s s是符号位, e e e是8位指数, f f f是23位尾数。

对于双精度浮点数:

V a l u e = ( − 1 ) s × 2 e − 1023 × ( 1. f ) Value = (-1)^s \times 2^{e-1023} \times (1.f) Value=(1)s×2e1023×(1.f)

其中 s s s是符号位, e e e是11位指数, f f f是52位尾数。

浮点异常处理由FPSCR(浮点状态控制寄存器)控制,包含标志位:

F P S C R = { N , Z , C , V , Q C , D N , F Z , R M o d e [ 1 : 0 ] , S t r i d e [ 1 : 0 ] , I D E , I X E , U F E , O F E , D Z E , I O E } FPSCR = \{N, Z, C, V, QC, DN, FZ, RMode[1:0], Stride[1:0], IDE, IXE, UFE, OFE, DZE, IOE\} FPSCR={N,Z,C,V,QC,DN,FZ,RMode[1:0],Stride[1:0],IDE,IXE,UFE,OFE,DZE,IOE}

其中 R M o d e RMode RMode控制舍入模式:

  • R M o d e = 00 RMode = 00 RMode=00:舍入到最近(偶数)
  • R M o d e = 01 RMode = 01 RMode=01:舍入到正无穷
  • R M o d e = 10 RMode = 10 RMode=10:舍入到负无穷
  • R M o d e = 11 RMode = 11 RMode=11:舍入到零

浮点运算的精度损失可以通过以下公式量化:

ε r e l = ∣ x e x a c t − x c o m p u t e d ∣ ∣ x e x a c t ∣ ≤ 2 − 23 \varepsilon_{rel} = \frac{|x_{exact} - x_{computed}|}{|x_{exact}|} \leq 2^{-23} εrel=xexactxexactxcomputed223 (单精度)
ε r e l = ∣ x e x a c t − x c o m p u t e d ∣ ∣ x e x a c t ∣ ≤ 2 − 52 \varepsilon_{rel} = \frac{|x_{exact} - x_{computed}|}{|x_{exact}|} \leq 2^{-52} εrel=xexactxexactxcomputed252 (双精度)

NEON寄存器和高级SIMD

ARM的NEON技术是一种SIMD(单指令多数据)扩展,允许同时对多个数据元素执行相同操作。在ARMv7中,NEON与VFP共享寄存器,但提供更多的排列组合。在ARMv8中,NEON使用V0-V31寄存器。

NEON寄存器可以包含不同类型和大小的数据元素。例如,一个128位V寄存器可以包含:

  • 16个8位元素
  • 8个16位元素
  • 4个32位元素
  • 2个64位元素

SIMD运算可以表达为:

V d [ i ] = V n [ i ] ⊗ V m [ i ] V_d[i] = V_n[i] \otimes V_m[i] Vd[i]=Vn[i]Vm[i] 对于 i ∈ [ 0 , l a n e s − 1 ] i \in [0, lanes-1] i[0,lanes1]

其中 ⊗ \otimes 表示任意运算(加、减、乘等), l a n e s lanes lanes是寄存器中的通道数量,取决于数据类型。

ARMv8.2引入了半精度(16位)浮点支持,SVE(可扩展矢量扩展)引入了可变长度的矢量寄存器,长度可以从128位到2048位不等。SVE寄存器Z0-Z31的理论算力可以计算为:

F L O P S p e a k = f C P U × l a n e s × o p e r a t i o n s _ p e r _ c y c l e FLOPS_{peak} = f_{CPU} \times lanes \times operations\_per\_cycle FLOPSpeak=fCPU×lanes×operations_per_cycle

其中 l a n e s = S V E _ V e c t o r _ L e n g t h e l e m e n t _ s i z e lanes = \frac{SVE\_Vector\_Length}{element\_size} lanes=element_sizeSVE_Vector_Length

矢量化优化的理论加速比:

S p e e d u p = T s c a l a r T v e c t o r ≈ n n / v + o v e r h e a d Speedup = \frac{T_{scalar}}{T_{vector}} \approx \frac{n}{n/v + overhead} Speedup=TvectorTscalarn/v+overheadn

其中 n n n是元素数量, v v v是SIMD宽度, o v e r h e a d overhead overhead是矢量化开销。

NEON提供了丰富的数据排列和重组指令,如TBL(表查找),可以表示为:

V d [ i ] = { 0 , 如果 V n [ V m [ i ] ] > elements V n [ V m [ i ] ] , 其他情况 V_d[i] = \begin{cases} 0, & \text{如果$V_n[V_m[i]] > \text{elements}$} \\ V_n[V_m[i]], & \text{其他情况} \end{cases} Vd[i]={0,Vn[Vm[i]],如果Vn[Vm[i]]>elements其他情况

系统寄存器和内存管理

ARM架构包含许多系统寄存器,用于控制处理器功能、内存管理、异常处理等。这些寄存器通常只能在特权模式下访问。

在ARMv8中,系统寄存器使用统一的编码方案:

S < o p 0 > < o p 1 > < C n > < C m > < o p 2 > S____ S<op0><op1><Cn><Cm><op2>

例如,系统控制寄存器SCTLR_EL1被编码为S3_0_C1_C0_0。

内存管理寄存器

ARMv8的虚拟内存系统通过以下关键寄存器控制:

TTBRx_EL1:页表基地址寄存器,存储虚拟地址转换表的基地址。虚拟地址到物理地址的映射可以表达为:

P A = P a g e T a b l e _ L o o k u p ( V A , T T B R _ B A S E ) PA = PageTable\_Lookup(VA, TTBR\_BASE) PA=PageTable_Lookup(VA,TTBR_BASE)

其中查找过程是递归的,对于48位地址空间,使用4级页表:

L 0 _ i n d e x = ( V A > > 39 ) & 0 x 1 F F L0\_index = (VA >> 39) \& 0x1FF L0_index=(VA>>39)&0x1FF
L 1 _ i n d e x = ( V A > > 30 ) & 0 x 1 F F L1\_index = (VA >> 30) \& 0x1FF L1_index=(VA>>30)&0x1FF
L 2 _ i n d e x = ( V A > > 21 ) & 0 x 1 F F L2\_index = (VA >> 21) \& 0x1FF L2_index=(VA>>21)&0x1FF
L 3 _ i n d e x = ( V A > > 12 ) & 0 x 1 F F L3\_index = (VA >> 12) \& 0x1FF L3_index=(VA>>12)&0x1FF
O f f s e t = V A & 0 x F F F Offset = VA \& 0xFFF Offset=VA&0xFFF

MAIR_EL1:内存属性间接寄存器,定义内存访问的缓存策略。内存访问延迟可以表示为:

T a c c e s s = { T c a c h e , 如果缓存命中 T c a c h e + p × T m e m o r y , 如果缓存未命中 T_{access} = \begin{cases} T_{cache}, & \text{如果缓存命中} \\ T_{cache} + p \times T_{memory}, & \text{如果缓存未命中} \end{cases} Taccess={Tcache,Tcache+p×Tmemory,如果缓存命中如果缓存未命中

其中 p p p是缓存未命中的概率,与MAIR配置有关。

TCR_EL1:转换控制寄存器,控制虚拟地址转换的各种参数。虚拟地址空间大小由TCR的T0SZ和T1SZ字段控制:

V A _ S i z e = 64 − T 0 S Z VA\_Size = 64 - T0SZ VA_Size=64T0SZ (对于TTBR0_EL1映射的空间)
V A _ S i z e = 64 − T 1 S Z VA\_Size = 64 - T1SZ VA_Size=64T1SZ (对于TTBR1_EL1映射的空间)

TLB和缓存控制

转译后备缓冲区(TLB)加速地址转换过程,TLB命中率可以表示为:

H i t _ R a t e T L B = N h i t N t o t a l Hit\_Rate_{TLB} = \frac{N_{hit}}{N_{total}} Hit_RateTLB=NtotalNhit

TLB未命中惩罚可以表示为:

P e n a l t y T L B _ m i s s = T p a g e _ w a l k = ∑ i = 0 l e v e l s − 1 T m e m o r y _ a c c e s s Penalty_{TLB\_miss} = T_{page\_walk} = \sum_{i=0}^{levels-1} T_{memory\_access} PenaltyTLB_miss=Tpage_walk=i=0levels1Tmemory_access

ARMv8提供了复杂的缓存管理指令,如DC CIVAC(数据缓存清除并使无效到PoC)。缓存一致性维护的理论模型可以表示为:

T i m e T o G l o b a l V i s i b i l i t y = m a x ( T i n t e r c o n n e c t _ l a t e n c y , T l o c a l _ c a c h e _ f l u s h ) TimeToGlobalVisibility = max(T_{interconnect\_latency}, T_{local\_cache\_flush}) TimeToGlobalVisibility=max(Tinterconnect_latency,Tlocal_cache_flush)

性能监控寄存器

ARMv8引入了PMU(性能监控单元),通过一系列寄存器提供性能计数功能:

PMCR_EL0:性能监控控制寄存器,控制计数器的全局启用/禁用。

PMCCNTR_EL0:周期计数器,计算执行的CPU周期数。程序执行时间可以表示为:

T e x e c u t i o n = P M C C N T R _ E L 0 f C P U T_{execution} = \frac{PMCCNTR\_EL0}{f_{CPU}} Texecution=fCPUPMCCNTR_EL0

PMXEVCNTR_EL0:事件计数器,可以计数多种微架构事件。CPI(每指令周期数)可以计算为:

C P I = P M C C N T R _ E L 0 P M X E V C N T R _ E L 0 ( 指令计数 ) CPI = \frac{PMCCNTR\_EL0}{PMXEVCNTR\_EL0(\text{指令计数})} CPI=PMXEVCNTR_EL0(指令计数)PMCCNTR_EL0

使用这些寄存器,可以构建详细的性能分析模型:

T e x e c u t i o n = N i n s t r u c t i o n s × C P I × 1 f C P U T_{execution} = N_{instructions} \times CPI \times \frac{1}{f_{CPU}} Texecution=Ninstructions×CPI×fCPU1

其中 C P I CPI CPI可以进一步分解为:

C P I = C P I b a s e + C P I m e m o r y + C P I b r a n c h + C P I d e p e n d e n c i e s CPI = CPI_{base} + CPI_{memory} + CPI_{branch} + CPI_{dependencies} CPI=CPIbase+CPImemory+CPIbranch+CPIdependencies

寄存器的使用约定

ARM架构定义了标准的调用约定,规定了寄存器在函数调用中的使用方式。在ARM AAPCS(ARM架构过程调用标准)中:

  • R0-R3(ARMv7)或X0-X7(ARMv8)用于传递参数
  • R0(ARMv7)或X0(ARMv8)用于返回值
  • 调用者保存的寄存器必须由调用函数保存和恢复
  • 被调用者保存的寄存器必须由被调用函数保存和恢复

函数调用的栈帧动态演化可以表示为:

f r a m e c a l l e r → p u s h ( L R ) → a l l o c a t e ( f r a m e c a l l e e ) → f u n c t i o n _ b o d y → d e a l l o c a t e ( f r a m e c a l l e e ) → p o p ( L R ) → f r a m e c a l l e r frame_{caller} \rightarrow push(LR) \rightarrow allocate(frame_{callee}) \rightarrow function\_body \rightarrow deallocate(frame_{callee}) \rightarrow pop(LR) \rightarrow frame_{caller} framecallerpush(LR)allocate(framecallee)function_bodydeallocate(framecallee)pop(LR)framecaller

在尾调用优化中,如果函数B是函数A的最后一个调用,则可以重用A的栈帧,避免创建B的新栈帧:

f r a m e A → . . . → f u n c t i o n _ B _ b o d y → d e a l l o c a t e ( f r a m e A ) → r e t u r n frame_A \rightarrow ... \rightarrow function\_B\_body \rightarrow deallocate(frame_A) \rightarrow return frameA...function_B_bodydeallocate(frameA)return

这样可以将递归调用优化为迭代形式,从 O ( n ) O(n) O(n)空间复杂度优化到 O ( 1 ) O(1) O(1)

寄存器的数学运算示例

整数加法及溢出检测

ADDS X0, X1, X2  ; X0 = X1 + X2,更新标志位

数学表达式: X 0 = X 1 + X 2 X0 = X1 + X2 X0=X1+X2,同时:

N = X 0 [ 63 ] N = X0[63] N=X0[63]
Z = ( X 0 = = 0 ) ? 1 : 0 Z = (X0 == 0) ? 1 : 0 Z=(X0==0)?1:0
C = ( X 1 + X 2 > 2 64 − 1 ) ? 1 : 0 C = (X1 + X2 > 2^{64} - 1) ? 1 : 0 C=(X1+X2>2641)?1:0
V = ( ( X 1 [ 63 ] = = X 2 [ 63 ] ) ∧ ( X 0 [ 63 ] ≠ X 1 [ 63 ] ) ) ? 1 : 0 V = ((X1[63] == X2[63]) \land (X0[63] \neq X1[63])) ? 1 : 0 V=((X1[63]==X2[63])(X0[63]=X1[63]))?1:0

条件执行的乘法

MULLT R0, R1, R2  ; 如果N≠V,则R0 = R1 × R2

数学表达式: R 0 = R 1 × R 2 R0 = R1 \times R2 R0=R1×R2 当且仅当 N ≠ V N \neq V N=V

对于64位乘法,结果可能是128位:

MUL X0, X1, X2      ; X0 = low64(X1 × X2)
UMULH X3, X1, X2    ; X3 = high64(X1 × X2)

完整结果表示为: r e s u l t = X 3 × 2 64 + X 0 result = X3 \times 2^{64} + X0 result=X3×264+X0

浮点向量运算

FMLA V0.4S, V1.4S, V2.4S  ; V0.4S[i] = V0.4S[i] + (V1.4S[i] × V2.4S[i]) 对于i∈[0,3]

数学表达式: V 0.4 S [ i ] = V 0.4 S [ i ] + ( V 1.4 S [ i ] × V 2.4 S [ i ] ) V0.4S[i] = V0.4S[i] + (V1.4S[i] \times V2.4S[i]) V0.4S[i]=V0.4S[i]+(V1.4S[i]×V2.4S[i]) 对于 i ∈ [ 0 , 3 ] i \in [0,3] i[0,3]

向量化卷积运算可以表示为:

O u t p u t [ i ] = ∑ j = 0 k e r n e l _ s i z e − 1 I n p u t [ i + j ] × K e r n e l [ j ] Output[i] = \sum_{j=0}^{kernel\_size-1} Input[i+j] \times Kernel[j] Output[i]=j=0kernel_size1Input[i+j]×Kernel[j]

转换为NEON指令后,使用循环展开和并行计算显著提高性能。

寄存器优化技术

寄存器是最快的存储资源,因此优化寄存器使用对性能至关重要。一些关键优化技术包括:

寄存器分配

编译器尝试将变量分配到寄存器中以减少内存访问。优化问题可以表示为:

最小化 ∑ i = 1 n c o s t ( v i ) × m e m _ a c c e s s ( v i ) \sum_{i=1}^{n} cost(v_i) \times mem\_access(v_i) i=1ncost(vi)×mem_access(vi)

其中 v i v_i vi是变量, c o s t cost cost是访问内存的成本, m e m _ a c c e s s mem\_access mem_access是内存访问次数。这是一个NP完全问题,可以通过图着色算法近似解决。对于具有干涉的变量 v i v_i vi v j v_j vj,不能分配到同一个寄存器,构建干涉图:

G = ( V , E ) G = (V, E) G=(V,E),其中 V V V是变量集合, E = { ( v i , v j ) ∣ l i f e t i m e ( v i ) ∩ l i f e t i m e ( v j ) ≠ ∅ } E = \{(v_i, v_j) | lifetime(v_i) \cap lifetime(v_j) \neq \emptyset\} E={(vi,vj)lifetime(vi)lifetime(vj)=}

然后使用启发式算法为图着色,颜色数量不超过可用寄存器数量。

循环展开与软件流水线

循环展开通过减少循环控制开销来优化寄存器使用。例如,原始循环:

for (i = 0; i < 100; i++) {
    sum += array[i];
}

展开后:

for (i = 0; i < 100; i += 4) {
    sum1 += array[i];
    sum2 += array[i+1];
    sum3 += array[i+2];
    sum4 += array[i+3];
}
sum = sum1 + sum2 + sum3 + sum4;

软件流水线将循环迭代重叠执行,可以表示为:

i t e r a t i o n n s t a g e 1 ∣ ∣ i t e r a t i o n n − 1 s t a g e 2 ∣ ∣ i t e r a t i o n n − 2 s t a g e 3 iteration_n^{stage_1} || iteration_{n-1}^{stage_2} || iteration_{n-2}^{stage_3} iterationnstage1∣∣iterationn1stage2∣∣iterationn2stage3

其中 ∣ ∣ || ∣∣表示并行执行。理论加速比为:

S p e e d u p = s t a g e s × i t e r a t i o n s s t a g e s + i t e r a t i o n s − 1 ≈ s t a g e s Speedup = \frac{stages \times iterations}{stages + iterations - 1} \approx stages Speedup=stages+iterations1stages×iterationsstages (当 i t e r a t i o n s ≫ s t a g e s iterations \gg stages iterationsstages时)

寄存器重命名和乱序执行

现代ARM处理器采用寄存器重命名技术解决写后读(WAR)和写后写(WAW)依赖问题。物理寄存器数量通常多于架构寄存器数量:

n p h y s i c a l > n a r c h i t e c t u r a l n_{physical} > n_{architectural} nphysical>narchitectural

重命名过程可以表示为映射函数:

m a p : R a r c h × v e r s i o n → R p h y s map: R_{arch} \times version \rightarrow R_{phys} map:Rarch×versionRphys

乱序执行的指令吞吐量可以表示为:

I P C m a x = m i n ( i s s u e _ w i d t h , n r e a d y c y c l e ) IPC_{max} = min(issue\_width, \frac{n_{ready}}{cycle}) IPCmax=min(issue_width,cyclenready)

其中 n r e a d y n_{ready} nready是每个周期准备好执行的指令数量,取决于依赖关系图的临界路径。

寄存器溢出和栈帧优化

当寄存器分配无法将所有活跃变量映射到物理寄存器时,需要将一些变量溢出到栈上。溢出成本可以表示为:

C o s t s p i l l = f r e q ( v ) × ( c o s t s t o r e + c o s t l o a d ) Cost_{spill} = freq(v) \times (cost_{store} + cost_{load}) Costspill=freq(v)×(coststore+costload)

其中 f r e q ( v ) freq(v) freq(v)是变量 v v v的访问频率。

ARM编译器通常采用线性扫描或图着色算法选择溢出变量,最小化总体溢出成本。栈帧结构优化可以减少内存访问:

f r a m e _ s i z e = ∑ i s i z e ( l o c a l _ v a r i ) + ∑ j s i z e ( s p i l l e d _ v a r j ) + s i z e ( s a v e d _ r e g i s t e r s ) + s i z e ( a l i g n m e n t _ p a d d i n g ) frame\_size = \sum_{i} size(local\_var_i) + \sum_{j} size(spilled\_var_j) + size(saved\_registers) + size(alignment\_padding) frame_size=isize(local_vari)+jsize(spilled_varj)+size(saved_registers)+size(alignment_padding)

现代编译器使用堆栈合并技术,分析变量生命周期,重用栈空间,优化公式为:

f r a m e _ s i z e o p t = m a x t i m e p o i n t { ∑ v ∣ a l i v e ( v , t i m e p o i n t ) s i z e ( v ) } + s i z e ( s a v e d _ r e g i s t e r s ) + s i z e ( a l i g n m e n t _ p a d d i n g ) frame\_size_{opt} = max_{timepoint} \{ \sum_{v | alive(v, timepoint)} size(v) \} + size(saved\_registers) + size(alignment\_padding) frame_sizeopt=maxtimepoint{valive(v,timepoint)size(v)}+size(saved_registers)+size(alignment_padding)

你可能感兴趣的:(arm开发,嵌入式硬件)