ARM 汇编 B 与 BL 的区别详解
核心区别
- 功能:B是无条件(或有条件)分支,仅改变程序流;BL(Branch with Link)在跳转前会把“下一条指令地址”写入R14(LR,链接寄存器),用于子程序调用后的返回。
- 返回机制:使用BL调用后,通常用MOV PC, LR或BX LR返回;B不保存返回地址,不能用于标准函数调用返回。
- 典型用途:B常用于段间跳转、条件分支、长循环或“永不返回”的入口;BL用于函数/子程序调用。
编码与寻址要点
- 语法:B{L}{}。是否保存返回地址由L决定; 为可选条件码(如EQ、NE),因此存在BEQ/BNE等条件分支。
- 目标地址计算:指令中携带24 位有符号立即数,先符号扩展为32 位,再左移 2 位(按字对齐),与PC相加得到目标地址;有效跳转范围约为±32 MB(2^25 字节)。
- PC 取值点:ARM 流水线使得取值时PC 通常指向当前指令地址 + 8;因此 BL 保存的是“下一条指令地址”(当前指令地址 + 4 的位置)。
典型用法与代码片段
- 使用BL调用函数并在函数尾部返回:
bl func ; 调用 func,返回地址自动写入 LR ; ... 其他代码 ... func: ; 若函数内还会调用别的函数,先保存 LR PUSH {R4-R7, LR} ; ... 函数体 ... POP {R4-R7, PC} ; 等价于 MOV PC, LR,函数返回 - 使用B实现循环或永不返回的分支:
loop: ; ... 循环体 ... b loop ; 跳回 loop(常见“死循环”写法) b reset ; 复位入口常用 B,因在 MMU/向量表早期阶段更稳妥 - 条件分支示例(与CMP配合):
cmp r0, #0 beq zero_handler bne non_zero_handler
建议
- LR 覆盖:在BL调用的子函数中若再调用别的函数,需先保存 LR(入栈),否则返回地址会被覆盖;函数尾部用POP {…, PC}或BX LR返回。
- 长跳转需求:B/BL是相对跳转,范围约±32 MB;若需跨更大地址空间,使用LDR PC, =label(文字池/绝对地址加载)或BX/BLX Rm等方式。
- 状态切换:仅BL/BX/BLX涉及指令集状态切换;BLX会根据目标地址最低位bit[0]自动在ARM/Thumb间切换,BX LR同样可用于从 Thumb 状态返回。
- 条件执行:B/BL都支持条件码(如BEQ/BNE),可与CMP/TST等指令组合实现高效分支。