寄存器

CPU除了有控制器、運(yùn)算器還有寄存器。其中寄存器的作用就是進(jìn)行數(shù)據(jù)的臨時(shí)存儲(chǔ)。
CPU的運(yùn)算速度是非??斓?,為了性能CPU在內(nèi)部開(kāi)辟一小塊臨時(shí)存儲(chǔ)區(qū)域,并在進(jìn)行運(yùn)算時(shí)先將數(shù)據(jù)從內(nèi)存復(fù)制到這一小塊臨時(shí)存儲(chǔ)區(qū)域中,運(yùn)算時(shí)就在這一小快臨時(shí)存儲(chǔ)區(qū)域內(nèi)進(jìn)行。我們稱這一小塊臨時(shí)存儲(chǔ)區(qū)域?yàn)榧拇嫫鳌?/p>
對(duì)于arm64系的CPU來(lái)說(shuō), 如果寄存器以x開(kāi)頭則表明的是一個(gè)64位的寄存器,如果以w開(kāi)頭則表明是一個(gè)32位的寄存器,其中32位的寄存器是64位寄存器的低32位部分并不是獨(dú)立存在的。注意在系統(tǒng)中沒(méi)有提供16位和8位的寄存器供訪問(wèn)和使用。
高速緩存(了解)
iPhoneX上搭載的ARM處理器A11它的1級(jí)緩存的容量是64KB,2級(jí)緩存的容量8M。
CPU每執(zhí)行一條指令前都需要從內(nèi)存中將指令讀取到CPU內(nèi)并執(zhí)行。而寄存器的運(yùn)行速度相比內(nèi)存讀寫(xiě)要快很多,為了性能,CPU還集成了一個(gè)高速緩存存儲(chǔ)區(qū)域.當(dāng)程序在運(yùn)行時(shí),先將要執(zhí)行的指令代碼以及數(shù)據(jù)復(fù)制到高速緩存中去(由操作系統(tǒng)完成).CPU直接從高速緩存依次讀取指令來(lái)執(zhí)行.
數(shù)據(jù)地址寄存器
數(shù)據(jù)地址寄存器通常用來(lái)做數(shù)據(jù)計(jì)算的臨時(shí)存儲(chǔ)、做累加、計(jì)數(shù)、地址保存等功能。定義這些寄存器的作用主要是用于在CPU指令中保存操作數(shù),在CPU中當(dāng)做一些常規(guī)變量來(lái)使用。
ARM64中
- 64位的數(shù)據(jù): X0-X30, XZR(零寄存器)
-
32位的數(shù)據(jù): W0-W30, WZR(零寄存器)
浮點(diǎn)寄存器
因?yàn)楦↑c(diǎn)數(shù)的存儲(chǔ)以及其運(yùn)算的特殊性,CPU中專門(mén)提供浮點(diǎn)數(shù)寄存器來(lái)處理浮點(diǎn)數(shù)
ARM64中
- 64位的數(shù)據(jù): D0 - D31
-
32位的數(shù)據(jù): S0 - S31
向量寄存器
現(xiàn)在的CPU支持向量運(yùn)算.(向量運(yùn)算在圖形處理相關(guān)的領(lǐng)域用得非常的多)為了支持向量計(jì)算,系統(tǒng)也提供了眾多的向量寄存器
128位的數(shù)據(jù):V0 - V31

棧
棧是一種具有特殊訪問(wèn)方式的存儲(chǔ)空間(特性:先進(jìn)后出 - First In Last Out,后進(jìn)先出 - Last In First Out)

函數(shù)調(diào)用會(huì)開(kāi)辟一段內(nèi)存空間(??臻g)用于函數(shù)的局部變量、參數(shù)和寄存器的保護(hù)
sub sp, sp,#0x20 ;拉伸棧空間
函數(shù)調(diào)用完畢之后也需要釋放空間
add sp, sp, #0x20 ;棧平衡
補(bǔ)充:sp拉伸空間的大小是根據(jù)什么?
函數(shù)的參數(shù)和局部變量的內(nèi)存空間大小 + x29、x30的內(nèi)存空間大小
拉伸空間的大小最少為16字節(jié),并且必須是16字節(jié)的倍數(shù)
sp 和 fp 寄存器
- sp寄存器在任意時(shí)刻會(huì)保存棧頂?shù)牡刂?/li>
- fp寄存器也稱為x29寄存器屬于通用寄存器,但是在某些時(shí)刻我們利用它保存棧底的地址
注意:
1.ARM64 開(kāi)始就取消了32位的 LDM,STM,PUSH,POP指令,取而代之的是str\stp、ldr\ldp
2.ARM64 里面對(duì)棧的操作是16字節(jié)對(duì)齊
內(nèi)存讀寫(xiě)指令
注意:讀/寫(xiě)數(shù)據(jù)都是往高地址讀/寫(xiě)
str(store register)指令
將數(shù)據(jù)從寄存器中讀出來(lái),存到內(nèi)存中
ldr(load register)指令
將數(shù)據(jù)從內(nèi)存中讀出來(lái),存到寄存器中
此 ldr 和 str 的變種 ldp 和 stp 還可以操作2個(gè)寄存器
棧操作
練習(xí):使用32個(gè)字節(jié)空間作為這段程序的??臻g,然后利用棧將x0和x1的值進(jìn)行交換
.text
.global _A
_A:
mov x0, #0xa ;x0=10
mov x1, #0xc ;x1=12
sub sp, sp,#0x20 ;拉伸??臻g
stp x0, x1, [sp, #0x10] ;sp棧偏移16字節(jié)寫(xiě)入x0,x1
ldp x1, x0, [sp, #0x10] ;sp棧偏移16字節(jié)讀取x1,x0
add sp, sp, #0x20 ;棧平衡(每個(gè)函數(shù)調(diào)用完畢之后,將拉伸的??臻g平衡(將sp加回去))
ret
bl 和 ret 指令
bl 指令--跳轉(zhuǎn)
- 將下一條指令的地址放入 lr(x30)寄存器,保存回家的地址
- 跳轉(zhuǎn)到標(biāo)號(hào)處執(zhí)行指令
rec 指令--返回
- 返回到 lr 寄存器所保存的地址
- pc 寄存器指向此地址,并執(zhí)行下一步指令
注意:x30寄存器存放的是函數(shù)的返回地址,當(dāng)ret指令執(zhí)行時(shí)刻,會(huì)尋找x30寄存器保存的地址值
函數(shù)的本質(zhì)
函數(shù)的參數(shù)
在 ARM64 中,函數(shù)的參數(shù)是保存在X0-X7(W0-W7)這8個(gè)寄存器里面,如果超過(guò)8個(gè)參數(shù),超過(guò)的參數(shù)會(huì)入棧,保存在棧里面。
調(diào)用函數(shù)一:(這種情況是參數(shù)個(gè)數(shù)不超過(guò)8)
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int sum1(int a, int b) {
return a + b;
}
int main(int argc, char * argv[]) {
sum1(10, 20);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 未調(diào)用函數(shù)時(shí)的匯編代碼

- 進(jìn)入函數(shù)后的匯編代碼

調(diào)用函數(shù)二:(這種情況是參數(shù)個(gè)數(shù)超過(guò)8)
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int sum2(int a, int b, int c, int d, int e, int f, int g, int h, int i) {
int sum = a + b + c + d + e + f + g + h + i;
return sum;
}
int main(int argc, char * argv[]) {
sum2(1, 2, 3, 4, 5, 6, 7, 8, 9);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 未調(diào)用函數(shù)時(shí)的匯編代碼

- 進(jìn)入函數(shù)后的匯編代碼

函數(shù)的局部變量
在 ARM64 中,函數(shù)的局部變量保存在棧里面。
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int sum3() {
int a = 10;
int b = 20;
return a + b;
}
int main(int argc, char * argv[]) {
sum3();
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
- 未調(diào)用函數(shù)時(shí)的匯編代碼

- 進(jìn)入函數(shù)后的匯編代碼

通過(guò)調(diào)用函數(shù)的前后匯編代碼可以發(fā)現(xiàn)函數(shù)的局部變量保存在棧里面,當(dāng)函數(shù)調(diào)用完之后,保持棧平衡,就是我們所謂的釋放內(nèi)存空間,其實(shí)真正意義上是此數(shù)據(jù)仍在內(nèi)存中,只是此時(shí)沒(méi)法直接訪問(wèn)到,等待下一次其他數(shù)據(jù)的覆蓋。
函數(shù)的返回值
在 ARM64 中,函數(shù)的返回值保存在 x0寄存器
函數(shù)的嵌套調(diào)用
注意事項(xiàng):
1.當(dāng)此函數(shù)為葉子函數(shù)時(shí),就不需要在對(duì) x29 和 x30 寄存器的保護(hù)。
葉子函數(shù):函數(shù)里面不再調(diào)用其他函數(shù)。
2.當(dāng)函數(shù)中的參數(shù)還有其他函數(shù)的引用時(shí),需要對(duì)參數(shù)入棧,進(jìn)行保護(hù),以防引起數(shù)據(jù)錯(cuò)誤。
葉子函數(shù)
//葉子函數(shù)
int funcA(int a, int b) {
return a+b;
}
//非葉子函數(shù)
void funcB() {
printf("haha"); //調(diào)用 printf 函數(shù)
}


明顯可以發(fā)現(xiàn)葉子函數(shù)并沒(méi)有做任何對(duì) x29 和 x30 寄存器的保護(hù)。
函數(shù)嵌套
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int funcA(int a, int b) {
return a+b;
}
int funcC(int a, int b) {
int c = funcA(a, b);
int d = funcA(a, b);
return c+d;
}
int main(int argc, char * argv[]) {
funcC(10, 20);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}


