You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

ARM架构下BX指令相较MOV pc,R的非Thumb互用优势探究

关于ARM架构中BX vs MOV pc,R的性能与设计考量

你提到Linux里优先用BX的汇编宏,确实有历史和架构设计层面的原因——虽然你的基准测试在多款现代ARM核上没测出明显差异,但还是得从分支预测硬件设计架构兼容性官方语义规范这几个角度拆解:

1. 早期ARM核的分支预测硬件限制

ARM1176的文档指出返回预测仅识别BX lr及部分加载指令,这是早期ARM核分支预测单元(BPU)的设计约束:这类核的返回栈预测器(RSP)只会把BX lr视为函数返回的标志性指令,而MOV pc, lr不会触发RSP的预测动作。

至于你的树莓派1测试没体现出差异,可能的原因包括:

  • 测试场景里的无效计算指令耗时远超过分支延迟,掩盖了分支预测的优势
  • 内核版本或编译器优化导致实际执行的指令和手写汇编存在差异
  • ARM1176的RSP本身预测精度有限,在特定负载下优势不明显

2. 全系列架构兼容性的前瞻性设计

Linux的宏优先用BX,本质是为了兼容全系列ARM核——哪怕现代核(Cortex-A72、Neoverse-N1)已经能识别MOV pc, lr作为返回指令,但早期核(比如ARM9、部分ARM11型号)做不到。用BX可以保证在所有支持ARMv4T及以上的核上都能获得最优的分支预测表现,不用针对不同核做分支处理。

另外,BX本身是ARM架构里标准的返回指令语义,ARM官方编程指南也推荐用BX lr(或BLX)作为函数返回,而MOV pc, lr更多是早期没有BX指令时的替代方案,属于“兼容式写法”而非“最优写法”。

3. 你的基准测试数据解读

从你给出的测试结果来看:

  • 在Cortex-A17、A72、Neoverse-N1这些现代核上,BX lrMOV pc, lr的性能几乎无差异,说明这些核的BPU已经能把两种指令都识别为返回操作,触发RSP预测
  • div_bx2(在r3上做无效计算)比div_bx(操作lr)快很多,这是因为lr是链接寄存器,部分核对lr的读写有额外的流水线约束,和BX/MOV本身无关
  • 移除BLX测试里的nop导致性能下降,是因为指令对齐和流水线填充的问题,和BX的分支预测无关

总结:优先用BX的理由(除Thumb互用外)

  • 历史核的分支预测支持:早期ARM核的返回栈预测只识别BX lr,用它能保证在老核上获得更好的性能
  • 官方语义的规范性:ARM架构规范把BX定义为标准的分支/返回指令,MOV pc, R属于通用寄存器跳转,语义上更偏向“任意跳转”而非“函数返回”
  • 未来兼容性:哪怕现有核已经支持两种指令的预测,未来的ARM核可能会对标准语义的BX做更多优化,而MOV pc, R可能不会被优先考虑

补充测试代码与结果

核心测试代码(64字节对齐)

; 在lr寄存器上执行无效计算并通过BX返回
div_bx
mov r9, #2
mul lr, r9, lr
udiv lr, lr, r9
mul lr, r9, lr
udiv lr, lr, r9
bx lr

; 在其他寄存器上执行无效计算并通过BX返回
div_bx2
mov r9, #2
mul r3, r9, lr
udiv r3, r3, r9
mul r3, r9, r3
udiv r3, r3, r9
bx lr

; 在lr寄存器上执行无效计算并通过MOV返回
div_mov
mov r9, #2
mul lr, r9, lr
udiv lr, lr, r9
mul lr, r9, lr
udiv lr, lr, r9
mov pc, lr

; 使用传统函数指针序列调用
movmov
push {lr}
loop
mov lr, pc
mov pc, r1
mov lr, pc
mov pc, r1
mov lr, pc
mov pc, r1
mov lr, pc
mov pc, r1
subs r0, r0, #1
bne loop
pop {pc}

; 使用BLX调用
blx
push {lr}
loop
nop
blx r1
nop
blx r1
nop
blx r1
nop
blx r1
subs r0, r0, #1
bne loop
pop {pc}

每1亿次循环耗时(秒)

Neoverse-N1 r3p1 (AWS c6g.medium)
mov+movblx
div_bx5.731.70
div_mov5.891.71
div_bx22.811.69
Cortex-A72 r0p3 (AWS a1.medium)
mov+movblx
div_bx5.321.63
div_mov5.391.58
div_bx22.791.63
Cortex-A17 r0p1 (ASUS C100P)
mov+movblx
div_bx12.525.69
div_mov12.525.75
div_bx25.515.56

扩展测试代码与结果

header: .string " Calle BL B Difference"
format: .string "%12s %7i %7i %11i\n"
.align
.global main
main:
push {r3-r5, lr}
adr r0, header
bl puts
@ Warm up
bl clock
mov r0, #0x40000000
1: subs r0, r0, #1
bne 1b
bl clock
.macro run_test test
2: bl 1f
nop
bl clock
mov r4, r0
ldr r0, =10000000
.balign 64
3: mov lr, pc
bl 1f
nop
mov lr, pc
bl 1f
nop
mov lr, pc
bl 1f
nop
subs r0, r0, #1
bne 3b
bl clock
mov r5, r0
ldr r0, =10000000
.balign 64
5: mov lr, pc
b 1f
nop
mov lr, pc
b 1f
nop
mov lr, pc
b 1f
nop
subs r0, r0, #1
bne 5b
bl clock
sub r2, r5, r4
sub r3, r0, r5
sub r0, r3, r2
str r0, [sp]
adr r1, 4f
ldr r0, =format
bl printf
b 2f
.ltorg
4: .string "\test"
.balign 64
1:
.endm
run_test mov
mov lr, lr
mov pc, lr
run_test bx
mov lr, lr
bx lr
run_test mov_mov
mov r2, lr
mov pc, r2
run_test mov_bx
mov r2, lr
bx r2
run_test pp_mov_mov
push {r1-r11, lr}
pop {r1-r11, lr}
mov r12, lr
mov pc, r12
run_test pp_mov_bx
push {r1-r11, lr}
pop {r1-r11, lr}
mov r12, lr
bx r12
run_test pp_mov_mov_f
push {r0-r11}
pop {r0-r11}
mov r12, lr
mov pc, r12
run_test pp_mov_bx_f
push {r0-r11}
pop {r0-r11}
mov r12, lr
bx r12
run_test pp_mov
push {r1-r11, lr}
pop {r1-r11, lr}
mov r12, lr
mov pc, lr
run_test pp_bx
push {r1-r11, lr}
pop {r1-r11, lr}
mov r12, lr
bx lr
run_test pp_mov_f
push {r0-r11}
pop {r0-r11}
mov r12, lr
bx lr
run_test pp_bx_f
push {r0-r11}
pop {r0-r11}
mov r12, lr
bx lr
run_test add_mov
nop
add r2, lr, #4
mov pc, r2
run_test add_bx
nop
add r2, lr, #4
bx r2
2: pop {r3-r5, pc}
Cortex-A17测试结果
Calle      BL       B    Difference
mov      94492  255882      161390
bx       94673  255752      161079
mov_mov  255872  255806         -66
mov_bx   255902  255796        -106
pp_mov_mov 506079 506132          53
pp_mov_bx  506108 506262         154
pp_mov_mov_f 439339 439436         97
pp_mov_bx_f  439437 439776        339
pp_mov   247941  495527      247586
pp_bx    247891  494873      246982
pp_mov_f 230846  422626      191780
pp_bx_f  230850  422772      191922
add_mov  255997  255896        -101
add_bx   255900  256288         388
ARM1176(树莓派1)测试结果
Calle      BL       B    Difference
mov     464367  464372           5
bx      464343  465104         761
mov_mov 464346  464417          71
mov_bx  464280  464577         297
pp_mov_mov 1073684 1074169         485
pp_mov_bx  1074009 1073832        -177
pp_mov_mov_f 769160 768757        -403
pp_mov_bx_f  769354 769368          14
pp_mov  885585 1030520       144935
pp_bx   885222 1032396       147174
pp_mov_f 682139  726129        43990
pp_bx_f 682431  725210        42779
add_mov 494061  493306        -755
add_bx  494080  493093        -987

内容的提问来源于stack exchange,提问作者Timothy Baldwin

火山引擎 最新活动