ARM寄存器组详解

ARM寄存器组详解

引言

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

ARM寄存器组概述

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

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

T访问=1fCPUT_{访问} = \frac{1}{f_{CPU}}T访问=fCPU1

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

T内存=nfCPUT_{内存} = \frac{n}{f_{CPU}}T内存=fCPUn

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

核心寄存器

通用寄存器

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

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

对于32位模式:
Address=Base_Register+OffsetAddress = Base\_Register + OffsetAddress=Base_Register+Offset

其中Base_RegisterBase\_RegisterBase_Register通常是R0-R14中的一个,OffsetOffsetOffset可以是立即数或其他寄存器的值。

对于64位模式:
Address=X_Register+(Offset×Scale)+Extend(Index_Register)×ScaleAddress = X\_Register + (Offset \times Scale) + Extend(Index\_Register) \times ScaleAddress=X_Register+(Offset×Scale)+Extend(Index_Register)×Scale

其中X_RegisterX\_RegisterX_Register通常是X0-X30中的一个,OffsetOffsetOffset可以是立即数,Index_RegisterIndex\_RegisterIndex_Register是另一个寄存器,ExtendExtendExtend是符号或零扩展函数,ScaleScaleScale是缩放因子(可以是1、2、4或8)。

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

PAC(ptr,key,modifier)=(ptr&∼mask)∣(hash(ptr,key,modifier)&mask)PAC(ptr, key, modifier) = (ptr \& \sim mask) | (hash(ptr, key, modifier) \& mask)PAC(ptr,key,modifier)=(ptr&mask)(hash(ptr,key,modifier)&mask)

其中ptrptrptr是原始指针,keykeykey是密钥,modifiermodifiermodifier是上下文信息,maskmaskmask定义了用于存储认证码的位。

特殊用途寄存器

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

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

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

P(PCt+1=target∣PCt=current)=f(history)P(PC_{t+1} = target | PC_t = current) = f(history)P(PCt+1=targetPCt=current)=f(history)

其中f(history)f(history)f(history)是基于历史执行路径的预测函数。

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

LR=PC+4LR = PC + 4LR=PC+4 (对于ARM指令集)
LR=PC+2LR = PC + 2LR=PC+2 (对于Thumb指令集)

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

Stack_Usage=∑i=1depthframe_sizeiStack\_Usage = \sum_{i=1}^{depth} frame\_size_iStack_Usage=i=1depthframe_sizei

其中frame_sizeiframe\_size_iframe_sizei是第iii层调用的栈帧大小。

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

入栈操作:SP=SP−sizeSP = SP - sizeSP=SPsize
出栈操作:SP=SP+sizeSP = SP + sizeSP=SP+size

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

SPmod  16=0SP \mod 16 = 0SPmod16=0

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

Valid_Target=(Instruction[31:0]==BTI_Encoding)Valid\_Target = (Instruction[31:0] == BTI\_Encoding)Valid_Target=(Instruction[31:0]==BTI_Encoding)

程序状态寄存器

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

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

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

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]\}CPSR={N,Z,C,V,Q,J,GE[3:0],IT[7:0],E,A,I,F,T,M[4:0]}

其中:

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

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

EQ:Z=1EQ: Z = 1EQ:Z=1
NE:Z=0NE: Z = 0NE:Z=0
CS/HS:C=1CS/HS: C = 1CS/HS:C=1
CC/LO:C=0CC/LO: C = 0CC/LO:C=0
MI:N=1MI: N = 1MI:N=1
PL:N=0PL: N = 0PL:N=0
VS:V=1VS: V = 1VS:V=1
VC:V=0VC: V = 0VC:V=0
HI:C=1∧Z=0HI: C = 1 \land Z = 0HI:C=1Z=0
LS:C=0∨Z=1LS: C = 0 \lor Z = 1LS:C=0Z=1
GE:N=VGE: N = VGE:N=V
LT:N≠VLT: N \neq VLT:N=V
GT:Z=0∧N=VGT: Z = 0 \land N = VGT:Z=0N=V
LE:Z=1∨N≠VLE: Z = 1 \lor N \neq VLE:Z=1N=V
AL:不考虑条件标志(永远执行)AL: 不考虑条件标志(永远执行)AL:不考虑条件标志(永远执行)

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

P(PSTATEt+1∣PSTATEt,Instructiont)={1,如果Instructiont导致PSTATEt变为PSTATEt+10,其他情况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。

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

PSTATE=SPSR_ELxPSTATE = SPSR\_ELxPSTATE=SPSR_ELx

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

Allowed_Return=(EL_target≤EL_current)∧(EL_target≥1∨(EL_target=0∧!is_secure_state))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)

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

MMU_Enabled=SCTLR_ELx.MMMU\_Enabled = SCTLR\_ELx.MMMU_Enabled=SCTLR_ELx.M
Alignment_Check=SCTLR_ELx.AAlignment\_Check = SCTLR\_ELx.AAlignment_Check=SCTLR_ELx.A
Data_Cache_Enabled=SCTLR_ELx.CData\_Cache\_Enabled = SCTLR\_ELx.CData_Cache_Enabled=SCTLR_ELx.C
Stack_Alignment_Check=SCTLR_ELx.SAStack\_Alignment\_Check = SCTLR\_ELx.SAStack_Alignment_Check=SCTLR_ELx.SA

异常链接寄存器(ELR_ELx)

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

ELR_ELx=PCexception+offsetELR\_ELx = PC_{exception} + offsetELR_ELx=PCexception+offset

其中offsetoffsetoffset取决于异常类型。

异常合成寄存器(ESR_ELx)

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

ESR_ELx={EC[5:0],IL,ISS[24:0]}ESR\_ELx = \{EC[5:0], IL, ISS[24:0]\}ESR_ELx={EC[5:0],IL,ISS[24:0]}

其中ECECEC是异常类,ILILIL是指令长度,ISSISSISS是特定于异常的症状信息。

浮点寄存器

VFP寄存器

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

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

加法:Sd=Sn+SmS_d = S_n + S_mSd=Sn+Sm
乘法:Sd=Sn×SmS_d = S_n \times S_mSd=Sn×Sm
乘加:Sd=Sn×Sm+SaS_d = S_n \times S_m + S_aSd=Sn×Sm+Sa

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

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

其中sss是符号位,eee是8位指数,fff是23位尾数。

对于双精度浮点数:

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

其中sss是符号位,eee是11位指数,fff是52位尾数。

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

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\}FPSCR={N,Z,C,V,QC,DN,FZ,RMode[1:0],Stride[1:0],IDE,IXE,UFE,OFE,DZE,IOE}

其中RModeRModeRMode控制舍入模式:

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

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

εrel=∣xexact−xcomputed∣∣xexact∣≤2−23\varepsilon_{rel} = \frac{|x_{exact} - x_{computed}|}{|x_{exact}|} \leq 2^{-23}εrel=xexactxexactxcomputed223 (单精度)
εrel=∣xexact−xcomputed∣∣xexact∣≤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运算可以表达为:

Vd[i]=Vn[i]⊗Vm[i]V_d[i] = V_n[i] \otimes V_m[i]Vd[i]=Vn[i]Vm[i] 对于i∈[0,lanes−1]i \in [0, lanes-1]i[0,lanes1]

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

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

FLOPSpeak=fCPU×lanes×operations_per_cycleFLOPS_{peak} = f_{CPU} \times lanes \times operations\_per\_cycleFLOPSpeak=fCPU×lanes×operations_per_cycle

其中lanes=SVE_Vector_Lengthelement_sizelanes = \frac{SVE\_Vector\_Length}{element\_size}lanes=element_sizeSVE_Vector_Length

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

Speedup=TscalarTvector≈nn/v+overheadSpeedup = \frac{T_{scalar}}{T_{vector}} \approx \frac{n}{n/v + overhead}Speedup=TvectorTscalarn/v+overheadn

其中nnn是元素数量,vvv是SIMD宽度,overheadoverheadoverhead是矢量化开销。

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

Vd[i]={0,如果Vn[Vm[i]]>elementsVn[Vm[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<op0><op1><Cn><Cm><op2>S<op0>_<op1>_<Cn>_<Cm>_<op2>S<op0><op1><Cn><Cm><op2>

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

内存管理寄存器

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

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

PA=PageTable_Lookup(VA,TTBR_BASE)PA = PageTable\_Lookup(VA, TTBR\_BASE)PA=PageTable_Lookup(VA,TTBR_BASE)

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

L0_index=(VA>>39)&0x1FFL0\_index = (VA >> 39) \& 0x1FFL0_index=(VA>>39)&0x1FF
L1_index=(VA>>30)&0x1FFL1\_index = (VA >> 30) \& 0x1FFL1_index=(VA>>30)&0x1FF
L2_index=(VA>>21)&0x1FFL2\_index = (VA >> 21) \& 0x1FFL2_index=(VA>>21)&0x1FF
L3_index=(VA>>12)&0x1FFL3\_index = (VA >> 12) \& 0x1FFL3_index=(VA>>12)&0x1FF
Offset=VA&0xFFFOffset = VA \& 0xFFFOffset=VA&0xFFF

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

Taccess={Tcache,如果缓存命中Tcache+p×Tmemory,如果缓存未命中T_{access} = \begin{cases} T_{cache}, & \text{如果缓存命中} \\ T_{cache} + p \times T_{memory}, & \text{如果缓存未命中} \end{cases}Taccess={Tcache,Tcache+p×Tmemory,如果缓存命中如果缓存未命中

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

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

VA_Size=64−T0SZVA\_Size = 64 - T0SZVA_Size=64T0SZ (对于TTBR0_EL1映射的空间)
VA_Size=64−T1SZVA\_Size = 64 - T1SZVA_Size=64T1SZ (对于TTBR1_EL1映射的空间)

TLB和缓存控制

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

Hit_RateTLB=NhitNtotalHit\_Rate_{TLB} = \frac{N_{hit}}{N_{total}}Hit_RateTLB=NtotalNhit

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

PenaltyTLB_miss=Tpage_walk=∑i=0levels−1Tmemory_accessPenalty_{TLB\_miss} = T_{page\_walk} = \sum_{i=0}^{levels-1} T_{memory\_access}PenaltyTLB_miss=Tpage_walk=i=0levels1Tmemory_access

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

TimeToGlobalVisibility=max(Tinterconnect_latency,Tlocal_cache_flush)TimeToGlobalVisibility = max(T_{interconnect\_latency}, T_{local\_cache\_flush})TimeToGlobalVisibility=max(Tinterconnect_latency,Tlocal_cache_flush)

性能监控寄存器

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

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

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

Texecution=PMCCNTR_EL0fCPUT_{execution} = \frac{PMCCNTR\_EL0}{f_{CPU}}Texecution=fCPUPMCCNTR_EL0

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

CPI=PMCCNTR_EL0PMXEVCNTR_EL0(指令计数)CPI = \frac{PMCCNTR\_EL0}{PMXEVCNTR\_EL0(\text{指令计数})}CPI=PMXEVCNTR_EL0(指令计数)PMCCNTR_EL0

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

Texecution=Ninstructions×CPI×1fCPUT_{execution} = N_{instructions} \times CPI \times \frac{1}{f_{CPU}}Texecution=Ninstructions×CPI×fCPU1

其中CPICPICPI可以进一步分解为:

CPI=CPIbase+CPImemory+CPIbranch+CPIdependenciesCPI = 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)用于返回值
  • 调用者保存的寄存器必须由调用函数保存和恢复
  • 被调用者保存的寄存器必须由被调用函数保存和恢复

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

framecaller→push(LR)→allocate(framecallee)→function_body→deallocate(framecallee)→pop(LR)→framecallerframe_{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的新栈帧:

frameA→...→function_B_body→deallocate(frameA)→returnframe_A \rightarrow ... \rightarrow function\_B\_body \rightarrow deallocate(frame_A) \rightarrow returnframeA...function_B_bodydeallocate(frameA)return

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

寄存器的数学运算示例

整数加法及溢出检测

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

数学表达式:X0=X1+X2X0 = X1 + X2X0=X1+X2,同时:

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

条件执行的乘法

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

数学表达式:R0=R1×R2R0 = R1 \times R2R0=R1×R2 当且仅当 N≠VN \neq VN=V

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

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

完整结果表示为:result=X3×264+X0result = X3 \times 2^{64} + X0result=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]

数学表达式:V0.4S[i]=V0.4S[i]+(V1.4S[i]×V2.4S[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]

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

Output[i]=∑j=0kernel_size−1Input[i+j]×Kernel[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=1ncost(vi)×mem_access(vi)\sum_{i=1}^{n} cost(v_i) \times mem\_access(v_i)i=1ncost(vi)×mem_access(vi)

其中viv_ivi是变量,costcostcost是访问内存的成本,mem_accessmem\_accessmem_access是内存访问次数。这是一个NP完全问题,可以通过图着色算法近似解决。对于具有干涉的变量viv_ivivjv_jvj,不能分配到同一个寄存器,构建干涉图:

G=(V,E)G = (V, E)G=(V,E),其中VVV是变量集合,E={(vi,vj)∣lifetime(vi)∩lifetime(vj)≠∅}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;

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

iterationnstage1∣∣iterationn−1stage2∣∣iterationn−2stage3iteration_n^{stage_1} || iteration_{n-1}^{stage_2} || iteration_{n-2}^{stage_3}iterationnstage1∣∣iterationn1stage2∣∣iterationn2stage3

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

Speedup=stages×iterationsstages+iterations−1≈stagesSpeedup = \frac{stages \times iterations}{stages + iterations - 1} \approx stagesSpeedup=stages+iterations1stages×iterationsstages (当iterations≫stagesiterations \gg stagesiterationsstages时)

寄存器重命名和乱序执行

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

nphysical>narchitecturaln_{physical} > n_{architectural}nphysical>narchitectural

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

map:Rarch×version→Rphysmap: R_{arch} \times version \rightarrow R_{phys}map:Rarch×versionRphys

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

IPCmax=min(issue_width,nreadycycle)IPC_{max} = min(issue\_width, \frac{n_{ready}}{cycle})IPCmax=min(issue_width,cyclenready)

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

寄存器溢出和栈帧优化

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

Costspill=freq(v)×(coststore+costload)Cost_{spill} = freq(v) \times (cost_{store} + cost_{load})Costspill=freq(v)×(coststore+costload)

其中freq(v)freq(v)freq(v)是变量vvv的访问频率。

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

frame_size=∑isize(local_vari)+∑jsize(spilled_varj)+size(saved_registers)+size(alignment_padding)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)

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

frame_sizeopt=maxtimepoint{∑v∣alive(v,timepoint)size(v)}+size(saved_registers)+size(alignment_padding)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)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值