[TOC]
补充
CPU
高速缓存,1级64k,2级8M
成本高,所以只有少的
高速缓存于PC内指令的关系??
寄存器 补充
8086中使用cs、ds、ss、es来保存地址段,而在<u>arm64中直接将sp直接指向,而不再需要段地址+偏移地址来寻址</u>
sub sp, sp, #0x20 ==> sp = sp - 0x20
[sp, #0x10] // 指针偏移到sp+0x10处
通用寄存器
64位:x0 - x30, XZR(零寄存器)
32位:w0 - w30, WZR(零寄存器)
栈
PC 寄存器(program counter)为指令寄存器,指示CPU当前要读取指令的地址;
拉伸向低地址,读写向高地址
sp 是x30寄存器?~~ x30是lr寄存器:lr是x30,存放函数返回的地址;sp是存放栈顶的地址;
<u>在以前,通过sp偏移一次存放一个数据,而在arm64中,是先用sp指示出一片区域,后通过sp的地址偏移来存放数据。</u>(因为内存空间够大了吧,先开辟后存放,不会浪费资源),所以现在对栈的判断就是通过sp的地址,sp指向的位置就是栈的空间。
fp是x29?是,在arm64中被弱化,基本没有用到
栈是存在于内存中的
sp 保存的是栈顶位置?那么栈底在什么位置,栈顶位置和栈底位置应该是固定不动的,否则函数栈的空间会进行变化?
sp是保存栈顶地址,对于栈底,在arm64中,调用一个函数sp会的值会相应的减少,相等于开辟栈空间,新开辟的栈空间即是新调用函数的空间,在函数调用结束后sp的值会相应的增加,可以保持栈的平衡,所以fp就被弱化。所以在内存中新调用函数时才会进行栈空间,调用结束就对栈进行平衡操作。
sp和fp定义了栈的空间,fp是x29寄存器,某时刻可以用来保存栈底的地址
str、ldr —变形升级—》 stp、ldp:操作两个寄存器
函数
bl和ret
寄存器读写
register write lr ***
register read lr ***
// 2种简写
[sp, #-0x10]! == >
sub sp, sp, #0x10
[sp],#0x10 == >
add sp, sp, #0x10
对齐? 对栈的操作(开辟空间/恢复平衡)必须保持为16的倍数
在函数有嵌套调用时需要将x30入栈,即现场保护;
参数&返回值
sp 拉伸是16对齐的,即16的倍数,指的是栈开辟的空间大小;不是指的数据的,数据在存入时可以任意大小地存入到所开辟的栈空间内;
在调用叶子函数时,不进行栈开辟操作,内部的临时变量是如何储存的?向低地址空间直接存储?那么其他函数不会覆盖的原因是什么? (视频参数&返回值,11’)
suma,,叶子函数不开辟空间解释
调用叶子函数(callee)时,sp的值已经固定了,因为是叶子函数所以后续没有其他函数调用,不会在开辟新的栈空间;因此,在叶子函数中的局部变量需要使用内存时,直接在sp的基础上进行向低地址偏移即可,而不会影响到调用函数(caller)的栈空间中的数据;而在叶子函数调用结束后数据都不在需要了,当有新其他函数进入时会将新的数据先写入栈中,然后在从其中读取出来。(多进程、多线程时如何保证)
函数返回值,通常存放在x0;void *是函数指针8个字节一个寄存器可以存储下; 返回类型可能为结构体,不能用x0;
参数存放在x0到x7这8个寄存器中,超过8个的参数所有参数都进行入栈操作,返回值通过放在x0;
需要入栈操作的情况下,不是一开始就将所有值都依次从x0开始存入,注意x0, x1需要先进行保存,而后在进入调用函数前将最后的两个参数复制给他们。
为啥将sp偏移过后的地址存放在x29中?相当于是对栈底的保护。
// 将x29、x30放入sp偏移16字节的空间中
// x29和x30,都是64位的,即8字节,8+8=16,整好都存入对应的地址中
stp x29,x30 [sp,#0x10]!
函数的局部变量
- 普通函数,参数个数小于8时,直接存放在x0-x7(或w0-w7)中;在参数个数超过8个会进行入栈,先对栈进行拉伸,后再通过sp向高地址写入数据;
- 叶子函数,参数直接在sp的基础上进行向下偏移进行储存;
- 嵌套调用时,如:
int funcA (int a, int b){
int d = sum(a, b);
int e = sum(a, b)
return e;
}
main() {
funcA(10,20);
}
此时需要对a,b这2个参数进行入栈保护,原因是首次传入时,x0,x1分别被赋值为10、20,这第一次调用sum后x0被赋值于返回值,所以无法再次获取10这个值,因此先保存到栈中后从栈中取出来。称之为现场保护。