You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ARM汇编实现C代码b=a/3(a=2)的原理及标签值疑问

解析ARM汇编实现无符号整数除以3的逻辑及标签含义

一、两段汇编代码的执行逻辑(a=2时)

对应的C代码为b = a/3,以下是针对不同无符号整数类型的汇编执行过程:

1. 无符号1字节版本(uint8_t)

当a=2时,2/3=0,汇编执行步骤如下:

ldrb    r2, [fp, #-6]   @ zero_extendqisi2  @ tmp162, a
  • 从栈帧[fp, #-6]位置加载无符号字节a=2,零扩展为32位值0x00000002存入r2。
ldr r3, .L34    @ tmp164,
  • 加载编译器预定义的乘法逆元常量到r3(该常量是编译器为无符号除以3计算的magic number,例如0xAAAAAAAB)。
umull   r1, r3, r2, r3  @, tmp163, tmp162, tmp164
  • 执行无符号64位乘法:r2 * r3,乘积的低32位存入r1,高32位存入r3。代入a=2计算:2 * 0xAAAAAAAB = 0x155555556,因此r3得到高32位值0x00000001。
mov r3, r3, lsr #1  @ tmp161, tmp163,
  • 将r3逻辑右移1位:0x00000001 >> 1 = 0x00000000,这就是2/3的商。
strb    r3, [fp, #-5]   @ tmp165, b
  • 将结果存入栈帧[fp, #-5]位置的变量b,值为0,符合预期。

核心逻辑:编译器用乘法逆元替代除法(除法指令执行效率远低于乘法)。对于无符号除数d,找到一个magic number m,使得m*d ≈ 2^n,这样a/d ≈ (a*m) >> n,通过乘法+移位得到商。

2. 无符号2字节版本(uint16_t)

当a=2时,2/3=0,汇编执行步骤如下:

ldrsh   r3, [fp, #-8]   @ tmp185, a
  • 从栈帧[fp, #-8]加载有符号半字a=2,符号扩展为32位0x00000002存入r3(因a是无符号正数,符号扩展不影响结果)。
ldr r2, .L51    @ tmp187,
  • 加载针对无符号半字除以3的乘法逆元常量到r2。
smull   r0, r2, r3, r2  @, tmp186, tmp185, tmp187
  • 执行有符号64位乘法:r3 * r2,低32位存入r0,高32位存入r2。代入a=2后,r2得到乘积的高32位(值为0)。
mov r3, r3, asr #31 @ tmp188, tmp185,
  • 将r3(a=2)算术右移31位:正数的算术右移结果为0x00000000(符号位为0)。
rsb r3, r3, r2  @ tmp184, tmp188, tmp186
  • 反向减法指令:r3 = r2 - r3,即0 - 0 = 0,得到最终的商。
strh    r3, [fp, #-6]   @ movhi @ tmp184, b
  • 将结果存入栈帧[fp, #-6]位置的变量b,值为0,符合预期。

核心逻辑:针对无符号半字,编译器复用了有符号乘法的逻辑,通过asr #31获取符号位(无符号数符号位为0),再用反向减法消除符号位对结果的影响,最终得到正确的商。

二、标签.L34、.L51的含义

.L34、.L51是编译器生成的局部常量池标签,它们指向内存中存储预定义常量的地址,而非程序计数器(PC)的位置。

当执行ldr r3, .L34这类指令时,CPU会从标签指向的内存区域加载编译器预先计算好的magic number(即前面提到的乘法逆元)。这些标签是汇编器在编译阶段用来管理常量的临时标识,最终会被替换成实际的内存地址,对应的数据段中存储的是优化除法所需的常量值。

内容的提问来源于stack exchange,提问作者Evan Lehr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 15:04:55