第5部分- Linux ARM彙編 ARM 架構細節

第5部分- Linux ARM彙編 ARM 架構細節

ARM處理器有37個寄存器,包括31個通用寄存器,和6個狀態寄存器。

通用寄存器是31個從x0-x30,31個數量是比較奇怪的,其實還有一個是Zero Register是wzr。如果是使用寄存器中的32位,就是w0-w30了。類型X86中的rax和eax寄存器,一個64位一個32位。

ARM處理器共有7種不同的處理器模式,在每一種處理器模式中有一組響應的寄存器組。

在AArch64時使用X30作爲子函數調用時使用的link register

在AArch32時始終使用LR作爲link register。

­­­ARM64通用寄存器

後續主要都是ARM64架構了,所以這裏以arm64例。

Register

Volatile?

Role

x0

Volatile

Parameter/scratch register 1, result register

x1-x7

Volatile

Parameter/scratch register 2-8

x8-x15

Volatile

Scratch registers

x16-x17

Volatile

Intra-procedure-call scratch registers

x18

Non-volatile

Platform register: in kernel mode, points to KPCR for the current processor; in user mode, points to TEB

x19-x28

Non-volatile

Scratch registers

x29/fp

Non-volatile

Frame pointer

x30/lr

Non-volatile

Link registers

 

 

 

可以通過w0 ~ w30來訪問這31個64位寄存器的低32位,寫入時會將高32位清零。

ARM64浮點寄存器

也是有32個浮點寄存器。

每個寄存器都可以作爲完整的128位值(通過v0-v31或q0-q31)進行訪問。 可以以64位值(通過d0-d31),32位值(通過s0-s31),16位值(通過h0-h31)或8位值進行訪問 (通過b0-b31)。 小於128位的訪問僅訪問整個128位寄存器的低位。 除非另有說明,否則它們保持其餘位不變。 (AArch64與AArch32不同,在AArch32中,較小的寄存器封裝在較大的寄存器的頂部。)

  • 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

Register

Volatile?

Role

v0

Volatile

Parameter/scratch register 1, result register

v1-v7

Volatile

Parameter/scratch registers 2-8

v8-v15

Non-volatile

Scratch registers (only the low 64 bits are non-volatile)

v16-v31

Volatile

Scratch registers

 

浮點控制寄存器

浮點控制寄存器(FPCR)對其中的各個位字段有某些要求:

Bits

Meaning

Volatile?

Role

26

AHP

Non-Volatile

Alternative half-precision control.

25

DN

Non-Volatile

Default NaN mode control.

24

FZ

Non-volatile

Flush-to-zero mode control.

23-22

RMode

Non-volatile

Rounding mode control.

15,12-8

IDE/IXE/etc

Non-Volatile

Exception trap enable bits, must always be 0.

32位浮點寄存器

浮點寄存器的單精度命名爲s0至s31,雙精度命名爲d0至d15。

這些寄存器分爲4個存儲區:s0–s7(d0–d3),s8–s15(d4–d7),s16–s23(d8–d11)和s24–s31(d12–d15)。

(存儲體0,s0–s7,d0–d3)稱爲標量存儲體,而其餘三個是矢量存儲體

VFPv2指令集

Vector Floating-point v2

可以通過軟件來實現VFPv2當然相比硬件,其性能會更差。

VFPv2提供了三個控制寄存器,其中一個稱爲fpscr, 該寄存器與cpsr相似,保留了通常的比較標誌N,Z,C和V。它還存儲了兩個非常有用的字段len和stride。 這兩個字段控制浮點指令的行爲。

            大多數VFPv2指令的格式爲vname Rdest,Rsource1,Rsource2或fname Rdest,Rsource1。 它們具有三種操作模式。

  • 標量。當目標寄存器位於存儲區0(s0–s7或d0–d3)中時,使用此模式。在這種情況下,該指令僅對Rsource1和Rsource2起作用。不涉及其他寄存器。
  • 矢量。當目標寄存器和Rsource2(或對於只有一個源寄存器的指令爲Rsource1)不在存儲區0中時,使用此模式。在這種情況下,指令將操作儘可能多的寄存器(從指令中的給定寄存器開始並環繞fpscr字段len中定義的寄存器組(至少1)。下一個操作的寄存器由fpscr的跨度字段定義(至少1)。如果發生折回,則任何寄存器都不能操作兩次。
  • 標量擴展(也稱爲混合矢量/標量)。如果Rsource2(如果指令只有一個源寄存器,則爲Rsource1)位於bank0中,而目的地則不是,則使用此模式。在這種情況下,Rsource2(或對於只有一個源的指令爲Rsource1)被固定爲源。其餘寄存器的操作與矢量情況一樣(即使用fpscr的len和stride)。

示例

// 假設len = 4, stride = 2
vadd.f32 s1, s2, s3  /* s1 ← s2 + s3. 標量操作,因爲s1在bank 0 */
vadd.f32 s1, s8, s15 /* s1 ← s8 + s15. 同上 */
vadd.f32 s8, s16, s24 /* s8  ← s16 + s24
                      s10 ← s18 + s26
                      s12 ← s20 + s28
                      s14 ← s22 + s30
                      矢量操作{s8,s10,s12,s14} ← {s16,s18,s20,s22} + {s24,s26,s28,s30}因爲Rdest and Rsource2 沒有在 bank 0
                   */
vadd.f32 s10, s16, s24 /* {s10,s12,s14,s8} ← {s16,s18,s20,s22} + {s24,s26,s28,s30}.*/
vadd.f32 s8, s16, s3 /* {s8,s10,s12,s14} ← {s16,s18,s20,s22} + {s3,s3,s3,s3}標量擴展,因爲Rsource2 在bank 0*/

 

Load和store

單精度是:vldr/vstr

加載/存儲的地址必須已經在通用寄存器中

vldr s1, [r3]         /* s1 ← *r3 */
vldr s2, [r3, #4]     /* s2 ← *(r3 + 4) */
vldr s3, [r3, #8]     /* s3 ← *(r3 + 8) */
vldr s4, [r3, #12]     /* s4 ← *(r3 + 12) */

 
vstr s10, [r4]        /* *r4 ← s10 */
vstr s11, [r4, #4]     /* *(r4 + 4) ← s11 */
vstr s12, [r4, #8]     /* *(r4 + 8) ← s12 */
vstr s13, [r4, #12]      /* *(r4 + 12) ← s13 */

可以Load/store多個寄存器:

vldm indexing-mode precision Rbase{!}, floating-point-register-set

vstm indexing-mode precision Rbase{!}, floating-point-register-set

vldmias r4, {s3-s8} /* s3 ← *r4
                       s4 ← *(r4 + 4)
                       s5 ← *(r4 + 8)
                       s6 ← *(r4 + 12)
                       s7 ← *(r4 + 16)
                       s8 ← *(r4 + 20)
                     其中i表示increase,a表示after,s表示單精度*/
vldmias r4!, {s3-s8} /* Like the previous instruction
                        最後r4 ← r4 + 24 ,r4會指向最後一個值。
                      */
vstmdbs r5!, {s12-s13} /*  *(r5 - 4 * 1) ← s12
                           *(r5 - 4 * 2) ← s13
                           r5 ← r5 - 4*2
                       */

32位中還有vpush

vpush {s0-s5} /* Equivalent to vstmdb sp!, {s0-s5} */
vpop {s0-s5}  /* Equivalent to vldmia sp!, {s0-s5} */

 

寄存器間移動

指令是vmov.

在一個通用寄存器和一個單精度寄存器之間vmov,數據不會轉換。 只有位會被複制,因此不要將浮點值與整數指令混合使用,反之亦然。

vmov d3, r4, r6  /* Lower32BitsOf(d3) ← r4
                    Higher32BitsOf(d3) ← r6
                 */
vmov r5, r7, d4 /* r5 ← Lower32BitsOf(d4)
                   r7 ← Higher32BitsOf(d4)
                 */

 

轉化

單精度轉換爲整型的時候丟失精度是必然的的,是多少問題。

指令:

vcvt

兩個寄存器都必須是浮點寄存器。浮點寄存器將包含一個不是IEEE 754值的值。

vcvt.f64.f32 d0, s0  /* 單精度s0 爲雙精度,並保存到d0 */
vcvt.f32.f64 s0, d0  /*雙精度d0 爲單精度s0,並保存到s0*/

 
vmov s0, r0          /* 從通用寄存器 r0 到浮點寄存器s0 */
vcvt.f32.s32 s0, s0  /* 將符號整型s0 爲單精度並保存到s0 */
vmov s0, r0          /*從通用寄存器 r0 到浮點寄存器s0 */
vcvt.f32.u32 s0, s0  /*將無符號整型s0 爲單精度並保存到s0 */

 
vmov s0, r0          /*從通用寄存器 r0 到浮點寄存器s0 */
vcvt.f64.s32 d0, s0  /*將符號整型s0 爲雙精度並保存到d0 */
vmov s0, r0          /*從通用寄存器 r0 到浮點寄存器s0 */
vcvt.f64.u32 d0, s0  /*將無符號整型s0 爲雙精度並保存到d0 */

 

修改fpscr

設置了len和stride的特殊寄存器fpscr無法直接修改。

必須使用vmrs指令將fpscr加載到通用寄存器中。 然後使用vmsr指令對寄存器進行操作,並將其移回fpscr。

len的值存儲在fpscr的第16至18位中。 len的值不直接存儲在這些位中。這是因爲len不能爲0(操作0個浮點數沒有意義)。 這樣,這些位中的值000表示len = 1,001表示len = 2,…,111表示len =8。以下是將len設置爲8的代碼。

/* Set the len field of fpscr to be 8 (bits: 111) */
mov r5, #7                     /* r5 ← 7. 7 is 111 in binary */
mov r5, r5, LSL #16                   /* r5 ← r5 << 16 */
vmrs r4, fpscr                        /* r4 ← fpscr */
orr r4, r4, r5                    /* r4 ← r4 | r5. Bitwise OR */
vmsr fpscr, r4                        /* fpscr ← r4 */

stride存儲在fpscr的20至21位中。 與len相似,這些位中的值00表示1,01表示 2,10表示 3,11表示 4。

 

https://developer.arm.com/docs/ddi0595/d/aarch32-system-registers/fpscr

該寄存器中的命名字段映射到AArch64 FPCR和FPSR中的等效字段。

寄存器FPEXC中,EN表示NEON和VFP是否使能。清零是關閉

要使能,將EN置1。

 

函數調用約定和浮點寄存器

  • fpscr的len和stride字段的所有位在函數輸入時均爲零,離開時這些位必須爲零。
  • 可以使用寄存器s0–s15和d0–d7傳遞浮點參數。 請注意,在單精度之後傳遞雙精度可能涉及丟棄奇數單精度寄存器(例如,可以使用s0和d1,但請注意,s1將不被使用)。
  • 所有其他浮點寄存器(s16-s31和d8-d15)在退出功能時必須保留其值。 可以使用vpush和vpop指令。
  • 如果函數返回浮點值,則返回寄存器將爲s0或d0。

注意有關可變參數函數(例如printf):不能將單精度浮點傳遞給此類函數之一。 只能通過雙精度。 需要將單精度值轉換爲雙精度值。 還要注意,通常使用整數寄存器(r0–r3),因此您最多隻能傳遞2個雙精度值,其餘的必須在堆棧上傳遞。 特別是對於printf,因爲r0包含字符串格式的地址,所以您只能在{r2,r3}中傳遞雙精度。

 

編譯

要將標誌-mfpu = vfpv2傳遞給as,否則將無法識別VFPv2指令。

 

浮點和整數傳輸

VMRS/VMSR在ARM寄存器與NEON和VFP系統寄存器之間傳輸內容。

fmrx/fmxr在ARM寄存器和VFP系統寄存器之間傳輸內容。

Fmrs/fmsr 在ARM寄存器和浮點寄存器之間傳輸內容。

 

ARM64系統寄存器

與AArch32一樣,AArch64規範提供了三個系統控制的“線程ID”寄存器:

Register

Role

TPIDR_EL0

Reserved.

TPIDRRO_EL0

Contains CPU number for current processor.

TPIDR_EL1

Points to KPCR structure for current processor.

程序計數器

pc,保存着當前CPU執行指令的地址。不能用作算數指令的源或目的地以及用作加載或存儲指令。

堆棧指針

sp,即x31,指向堆棧的頂部。sp不能被大多數指令引用, 但一些算術指令,例如ADD指令,可以讀寫當前的堆棧指針來調整函數中的堆棧指針。每個異常級別都有一個專用的SP寄存器。

fp,即x29,幀指針,指向當前frame的棧底,也就是高地址。

鏈接寄存器

lr,即x30,存儲着函數的返回地址。

程序狀態寄存器

在彙編中通過狀態寄存器來控制分支的執行。

cpsr:與其他寄存器不太一樣,其他寄存器用來存儲數據的,但是這個寄存器是,按位起作用的,每一位都有專門的含義。

spsr:當發生異常時,cpsr會存入spsr直到異常恢復再複製回cpsr。

特殊寄存器

運行模式

ARM處理器支持7種運行模式,分別是:

  • 用戶模式(usr):ARM處理器正常的程序運行狀態。
  • 快速中斷模式(flq):用於高速數據傳輸或通道處理。
  • 外部中斷模式(irq):用於通用的中斷處理。
  • 管理模式(svc):操作系統使用的保護模式。
  • 數據訪問終止模式(abt):當數據或指令預取終止時進入該模式,可用於虛擬存儲以及存儲保護。
  • 系統模式(sys):運行具有特短的操作系統任務。
  • 未定義指令終止模式(und):當未定義的指令執行時進入該模式。

各種處理器模式下的,通用寄存器

還有兩個工作模式:

hyp:用於虛擬化擴展。

monitor:用於Security擴展。

 

工作狀態

ARM狀態:執行32位字對齊的ARM指令。

Thumb狀態:執行16位字對齊的ARM指令。

Thumb狀態下的寄存器的命名與ARM有部分差異,它們的對應關係如下所示:

Thumb狀態下的R0~R7與ARM狀態下的R0~R7相同。

Thumb狀態下的CPSR與ARM狀態下的CPSR相同。

Thumb狀態下的FP與ARM狀態下的R11相同。

Thumb狀態下的IP與ARM狀態下的R12相同。

Thumb狀態下的SP與ARM狀態下的R13相同。

Thumb狀態下的LR與ARM狀態下的R14相同。

Thumb狀態下的PC與ARM狀態下的R15相同。

 

程序狀態寄存器

CPSR(當前程序狀態寄存器)可以在任何處理器模式下被訪問。

 

非對齊的存儲訪問操作

如果寫入到寄存器PC中的值是非字對齊的,要麼指令執行的結果不可預知,要麼地址值中最低兩位被忽略。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章