A64指令集基本指令(一):分支指令
前言
Alphabetical list of A64 base instructions gives full descriptions of the A64 instructions that are in the following instruction groups:
- Branch, Exception generation, and System instructions.
- Loads and stores associated with the general-purpose registers.
- Data processing (immediate).
- Data processing (register).
如 ARM
手册中所说,A64
基本指令包括
- 分支、异常生成和系统指令
- 与通用寄存器相关的加载和存储指令
- 数据处理(立即数)指令
- 数据处理(寄存器)指令
本文将详细介绍一下分支指令。另外,本系列目前不会涉及 SVE 指令和 SIMD 指令,避免有需要的读者浪费时间。
条件码
这个表格是 ARMv8-A
架构中 A64
指令集的条件码 (Condition code)
表,提供了条件分支指令使用的所有可能条件(手册原表中浮点数相关内容我已删掉)。
条件码 | 助记符 | 含义(整数) | 条件标志 |
---|---|---|---|
0000 | EQ | 相等 | Z == 1 |
0001 | NE | 不相等 | Z == 0 |
0010 | CS或HS | 进位设置 | C == 1 |
0011 | CC或LO | 进位清除 | C == 0 |
0100 | MI | 负数 | N == 1 |
0101 | PL | 正数或零 | N == 0 |
0110 | VS | 溢出 | V == 1 |
0111 | VC | 无溢出 | V == 0 |
1000 | HI | 无符号大于 | C == 1 && Z == 0 |
1001 | LS | 无符号小于等于 | !(C == 1 && Z == 0) |
1010 | GE | 有符号大于等于 | N == V |
1011 | LT | 有符号小于 | N != V |
1100 | GT | 有符号大于 | Z == 0 && N == V |
1101 | LE | 有符号小于等于 | !(Z == 0 && N == V) |
1110 | AL | 总是执行 | 任意 |
1111 | NV | 总是执行 | 任意 |
注:NV
条件码存在仅为提供 0b1111
编码的有效反汇编,其行为与 AL
相同。
标志位及含义对应解释
- N(Negative):结果是否为负数,N == 1,代表结果为负数
- Z(Zero):结果是否为零,Z == 1,代表结果为0
- C(Carry):产生进位/没有借位,C == 0,代表结果为负数
- V(oVerflow):有符号运算溢出,V == 1,代表结果溢出
注:在这几个标志位中,比较不同的是 C 标志位,C == 0,代表没有发生借位。而 C == 1代表发生了借位,为什么呢?
因为减法实际上是通过加法和补码来实现的。CMP A, B
语义上执行的是 A - B
。但我们知道,-B
的补码等于对 B
取反加1
,也就是实际上执行的是 A + (~B + 1)
,也就是 A + (B的二进制补码)
。
以一个 4bit
数据位宽的具体例子为例:假设比较 5
和 3
,(CMP 5, 3)
,实际执行: 5 + (~3 + 1) = 5 + 13 = 18 (超出4位,产生进位)。结果为正,且有进位,所以 C=1
,表示 5>3
。
条件指令
条件指令介绍开始之前需要知道,ARM架构对分支指令有严格的使用规则:
- 在调用嵌套子程序时,只能使用BLR或BL指令
- 从子程序返回时,只能使用RET指令
- 对于非子程序调用/返回的控制流转移,应使用B、B.cond等指令
1.条件分支指令
条件分支指令根据条件标志(Condition flags)或通用寄存器中的值改变执行流程。
示例指令:B.cond
- 用途:根据条件码执行条件跳转
- 执行过程:检查PSTATE中的条件标志(N、Z、C、V),如果满足条件,则PC += 偏移量(±1MB范围内)
- 例如:
B.EQ label
(如果结果为零则跳转)
示例指令:BC.cond
- 用途:一致性条件分支,保证跳转预测的一致性
- 执行过程:与B.cond类似,但提供更一致的分支预测行为
cond 所有取值如下所示,具体含义见条件码所示:
编码 | 0000 | 0001 | 0010 | 0011 | 0100 | 0101 | 0110 | 0111 | 1000 | 1001 | 1010 | 1011 | 1100 | 1101 | 1110 | 1111 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
cond | EQ | NE | CS | CC | MI | PL | VS | VC | HI | LS | GE | LT | GT | LE | AL | NV |
示例指令:CBNZ/CBZ
- 用途:比较寄存器值并在非零/零时分支
- 执行过程:将指定寄存器的值与零比较,满足条件则PC += 偏移量(±1MB范围内)
- 例如:
CBZ x0, label
(如果x0为零则跳转)
示例指令:TBNZ/TBZ
- 用途:测试寄存器中的特定位,并在非零/零时分支
- 执行过程:测试指定寄存器中特定位的值,满足条件则PC += 偏移量(±32KB范围内)
- 例如:
TBZ x0, #5, label
(如果x0的第5位为0则跳转)
2.无条件分支(立即数)
无条件分支指令无条件地改变执行流程,使用即时偏移量。
示例指令:B
- 用途:无条件跳转到目标地址
- 执行过程:PC += 偏移量(±128MB范围内)
- 例如:
B label
(总是跳转到label)
示例指令:BL
- 用途:带链接的分支,用于调用子程序
- 执行过程:将下一条指令地址保存到X30寄存器,然后PC += 偏移量(±128MB范围内)
- 例如:
BL function
(调用函数并保存返回地址)
3. 无条件分支(寄存器)
无条件分支指令通过寄存器中存储的地址值改变执行流程。
示例指令:BLR
- 用途:带链接的寄存器分支,用于间接调用子程序
- 执行过程:将下一条指令地址保存到
X30
,然后PC = 寄存器内容
- 例如:
BLR x0
(调用x0中保存的地址,并保存返回地址到X30)
示例指令:BR
- 用途:寄存器分支,无条件跳转到寄存器中的地址
- 执行过程:PC = 寄存器内容
- 例如:
BR x0
(跳转到x0中保存的地址)
示例指令:RET
- 用途:从子程序返回
- 执行过程:PC = X30(或指定寄存器)
- 例如:
RET
(返回到X30中保存的地址)
直接条件分支和间接条件分支指令
ARM
手册中还对分支指令做了直接分支和间接分支指令的区分。这里手册中另外介绍了很多关于地址认证的指令。不经常使用,不会详细展开。这部分其实也可以不看。
直接分支指令
直接分支指令中,跳转目标地址直接编码在指令中:
1. B (无条件分支)
- 用途:无条件跳转到指定地址
- 执行过程:PC直接设置为指令中的目标地址
- 示例:
B label1
—— 程序无条件跳转到label1处执行
2. B.cond (条件分支)
- 用途:根据条件标志位决定是否分支
- 执行过程:检查NZCV标志位,如果条件满足则跳转
- 示例:
B.EQ label2
—— 当结果为零(相等)时跳转到label2
3. BC.cond (条件一致性分支)
- 用途:带有内存一致性保证的条件分支
- 执行过程:与B.cond类似,但提供额外的一致性保证
- 示例:
BC.EQ label3
—— 在多处理器系统中提供一致性保证的条件分支
4. BL (带链接的分支)
- 用途:跳转到子程序并保存返回地址
- 执行过程:将返回地址保存在LR寄存器(X30)中,然后跳转
- 示例:
BL subroutine
—— 调用子程序并在完成后可返回
5. CBZ/CBNZ (比较并分支)
- 用途:将寄存器与零比较,根据结果分支
- 执行过程:
- CBZ:如果寄存器值为零则分支
- CBNZ:如果寄存器值非零则分支
- 示例:
CBZ X0, label4
—— 如果X0为0则跳转到label4CBNZ X1, label5
—— 如果X1不为0则跳转到label5
6. TBZ/TBNZ (测试位并分支)
- 用途:测试寄存器中特定位的值并分支
- 执行过程:
- TBZ:如果指定位是0则分支
- TBNZ:如果指定位是1则分支
- 示例:
TBZ X2, #3, label6
—— 如果X2的第3位是0则跳转TBNZ X3, #7, label7
—— 如果X3的第7位是1则跳转
间接分支指令
间接分支指令中,跳转目标地址存储在寄存器中:
1. BLR (带链接到寄存器的分支)
- 用途:跳转到寄存器指定的地址并保存返回地址
- 执行过程:将返回地址保存在LR中,然后跳转到寄存器指定的地址
- 示例:
BLR X4
—— 跳转到X4寄存器中存储的地址并保存返回地址
2. BLRAA/BLRAAZ/BLRAB/BLRABZ (带指针认证的链接到寄存器的分支)
- 用途:提供指针认证的间接调用
- 执行过程:验证跳转地址的完整性,如果通过,则设置返回地址并跳转
- 示例:
BLRAA X5, X6
—— 使用X6作为认证修饰符跳转到X5中的地址
3. BR (分支到寄存器)
- 用途:无条件跳转到寄存器中的地址
- 执行过程:PC设置为指定寄存器的值
- 示例:
BR X7
—— 跳转到X7寄存器包含的地址
4. BRAA/BRAAZ/BRAB/BRABZ (带指针认证的分支到寄存器)
- 用途:带指针认证的无条件间接跳转
- 执行过程:验证地址,如通过则执行跳转
- 示例:
BRAA X8, X9
—— 使用X9作为认证修饰符跳转到X8中的地址
5. ERET/ERETAA/ERETAB (异常返回)
- 用途:从异常处理返回
- 执行过程:从ELR_ELx寄存器加载PC并从SPSR_ELx还原处理器状态
- 示例:
ERET
—— 从异常处理返回到中断点
6. RET/RETAA/RETAB (子程序返回)
- 用途:从子程序返回
- 执行过程:将PC设置为LR寄存器(X30)中的返回地址
- 示例:
RET
—— 从子程序返回到调用点
7. RETAA/RETAB (带指针认证的返回)
- 用途:带指针认证的子程序返回
- 执行过程:验证LR中的返回地址,如通过则返回
- 示例:
RETAA
—— 使用A密钥认证LR并返回
8. RETAASPPC/RETABSPPC (使用偏移的带指针认证的返回)
- 用途:使用即时偏移的带指针认证的返回
- 执行过程:使用即时偏移进行指针认证并返回
- 示例:
RETAASPPC #16
—— 使用16字节偏移进行认证并返回
9. RETAASPPCR/RETABSPPCR (使用寄存器的带指针认证的返回)
- 用途:使用寄存器值进行指针认证的返回
- 执行过程:使用寄存器值作为修饰符进行指针认证并返回
- 示例:
RETAASPPCR X10
—— 使用X10寄存器值进行认证并返回
总结
完结撒花!!!
参考
a_a-profile_architecture_reference_manual.pdf