01.寄存器

寄存器是什么

CPU除了有控制器、运算器还有寄存器。其中寄存器的作用是进行数据的临时存储

CPU的运算速度非常快,为了性能CPU在内部开辟一小块零食存储区域,并在进行运算时先将数据从内存复制到这一小块临时区域中,运算时就在这一小块临时存储区域内进行。

高速缓存

CPU每执行一条指令前都要从内存中将指令读取到CPU内执行。而寄存器的运行速度相比内存读写要快很多,为了性能,CPU还集成了一个高速缓存存储区域当程序在运行时,先将需要执行的指令代码以及数据复制到高速缓存中去(由操作系统完成),CPU直接从高速缓存中依次读取指令来执行。

ARM64一个寄存器64位,8个字节

一:几种寄存器

  • 浮点寄存器

    • 因为浮点数的存储以及运算的特殊性,CPU中专门提供浮点寄存器来处理浮点数

      • 浮点寄存器64位:D0-D31 32位:S0-S31

  • 向量寄存器

    • 先在很多CPU支持向量运算(图形处理用的很多),为了支持向量计算而提供

      • 向量寄存器:128位 V0-V31

  • 异常状态寄存器

  • 通用寄存器(数据地址寄存器)

    • ARM64拥有32个64位的通用寄存器X0-X30以及XZR(零寄存器)

    • 为了兼容32位,所以ARM64拥有W0-W30和WZR30个32位寄存器

    • 32位寄存器并不是独立存在的。如:W0是X0的低32位

  • PC寄存器:指令指针寄存器

    • PC寄存器里面的值就是保存的CPU接下来需要执行的指令地址

    • 改变PC寄存器的值可以改变程序执行的流程

1.1 通用寄存器

  • x0-x7: 用于子程序调用时的参数传递,X0还用于返回值传递

  • x0 - x30是31个通用整形寄存器。每个寄存器可以存取一个64位大小的数。 当使用 r0 - r30访问时,它就是一个64位的数。当使用 w0 - w30访问时,访问的是这些寄存器的低32位,如图:

1.2 CPSR 状态寄存器

Current Program State Register

状态寄存器是32位的

  • CPSR的低8位(包括I、F、T和M[4:0])称为控制位,程序无法修改,除非CPU运行于特权模式下,程序才能修改控制位

  • N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行,意义重大

  • NZCV是状态寄存器的条件标志位,分别代表运算过程中产生的状态,其中:

    • N, negative condition flag,一般代表运算结果是负数

      • 1 代表结果为负数

      • 0 代表结果为非负数

    • Z, zero condition flag, 指令结果为0时Z=1,否则Z=0

    • C, carry condition flag, 一般情况下进行无符号数的运算

      • 加法运算

        • 当运算结果产生了进位(无符号溢出)C=1否则C=0

      • 减法运算(包括CMP)

        • 当运算是产生了借位时(无符号溢出)则C=0,否则C=1

        • 对于位数为N的无符号数来说其对应的二进制的最高位,即第N-1位就是他的最高有效位,而假想存在的第N位就是相对于最高有效位的更高位。

      • 进位

        • 我们知道,当两个数相加的时候,有可能从最高有效位向更高位进位。比如两个32位数据:0xaaaaaaaa + 0xaaaaaaaa,将会产生进位。但由于进位值在32位中无法保存,我们就只是简单的说这个进位值丢失了。其实CPU在运算的时候并不会丢弃这个进位值,而是记录在一个特殊的寄存器的某一位上。ARM上就用C为来记录这个进位值。

      • 借位

        • 当两个数据做减法的时候,有可能向更高位。比如:0x00000000 - 0x000000ff,将产生借位,借位后相当于计算0x100000000 - 0x000000ff = 0xffffff01,由于借了一位,所以C位用来标记借位。

  • V, oVerflow condition flag 有符号运算有溢出时,V=1

    • 正数 + 正数 = 负数 则 溢出

    • 负数 + 负数 = 正数 则 溢出

    • 正数 + 负数 不可能溢出

二:栈

后进先出 ARM64栈开口向底地址,即开辟的地址是朝底地址方向的

2.01 SP 和 FP寄存器

  • SP寄存器在任意时刻会保存栈顶的地址

  • FP寄存器也称为X29寄存器属于通用寄存器,但是在某些时候我们利用它保存栈底的地址

注意:ARM64开始,取消了32位的LDM、STM、PUSH、POP指令,取而代之的是:ldr、ldp、str、stp。在ARM64中对栈的操作是16字节对齐的

2.02 编译器会根据编译情况开辟固定的栈空间

2.03 函数调用栈

常见函数调用开辟和恢复栈空间

  • sub sp, sp, #0x40

    • 拉伸0x40(64字节)空间

    • 即sp寄存器地址减0x40

    • 开辟了64字节空间

  • stp x29, x30, [sp, #0x30]

    • x29、x30寄存器入栈保护

  • add x29, sp, #0x30

    • x29指向栈帧的底部

  • ldp x29, x30, [sp, #0x30]

    • 恢复x29、x30寄存器的值

  • add sp, sp, #0x40

    • 栈平衡,用完了要归位

  • ret

2.03 内存读写指令

读写:数据都是往高地址读写的 即:一段数据一个字节无法完整保存的话,连续的多个地址进行保存,地址递增

  • str 指令 store register

    • 将数据从寄存器中读出来,存到内存中

  • ldr 指令 load register

    • 将数据冲内存中读出来,存到寄存器中

  • ldp stp 可以一次性操作两个寄存器

2.04 bl 和 ret 指令

  • bl

    • 将下一条指令的地址放入lr(x30)寄存器

    • 转到标号处执行指令

  • ret

    • 默认使用lr(x30)寄存器的值,通过底层指令提示CPU此处作为下条指令地址

  • lr(x30)寄存器

    • 存放的是函数的返回地址,当ret指令执行时,回去寻找lr(x30)寄存器保存的地址

    • 在嵌套调用函数的时候需要将 x30 入栈

2.05 函数的参数和返回值

  • ARM64下,函数的参数是放在x0 ~ x7w0 ~ w7)这8个寄存器中。如果超过八个参数就会入栈。

  • 函数的返回值会放在X0寄存器中的。

  • 函数的局部变量是放在栈里面的

Last updated