摘要:前九篇从架构、ARM32、AArch64 寄存器/寻址、算术位操作、分支、调用约定、内存模型、NEON、SVE/SVE2 一路走到这里。这一篇回到标量浮点子系统:AArch64 用统一的 V0--V31 寄存器文件同时服务标量 FP、NEON 和 SVE;FPCR 控舍入/非规格化/异常陷阱,FPSR 记粘性异常标志;FMADD 单次舍入、FMAXNM 跳过 NaN、FRECPE/FRSQRTE 做快速倒数/平方根估计;最后落到 FP16/BF16 这些机器学习降精度扩展。
一、统一寄存器文件:H / S / D / V 是同一块物理存储
AArch64 的浮点和 NEON 共用 32 个 128 位寄存器 V0--V31,标量视图只是低宽度别名:
| 视图 | 宽度 | 用途 |
|---|---|---|
| Hn | 16 位 | FP16 / bfloat16 半精度 |
| Sn | 32 位 | binary32 单精度 |
| Dn | 64 位 | binary64 双精度 |
| Vn / Qn | 128 位 | NEON 向量全宽 |
零扩展铁律 :写 S0 会把 V0 的高 96 位清零;写 D0 清高 64 位。这样后续把 V0/Z0 当全宽读时,不会漏进上一轮 NEON/SVE 的脏数据。
标量 FP 完全符合 IEEE-754-2008:渐进下溢、正确舍入、五类异常(Invalid / DivideByZero / Overflow / Underflow / Inexact)齐全。
二、FPCR 与 FPSR:一个管"怎么算",一个管"算炸没炸"
两个系统寄存器用 MRS/MSR 访问,任何 EL 都能碰(受陷阱位影响):
1. FPCR(Floating-point Control Register)
- RMode23:22 :默认舍入模式
00=RN 最近偶(银行家)、01=RP 向 +∞、10=RM 向 −∞、11=RZ 向零 - FZ24:Flush-To-Zero,非规格化输入/输出清零。游戏/ML 常开(非规格化慢 10--100×),科学计算关。
- DN25:Default NaN,开则任何 NaN 运算回默认 NaN,不传播原 payload
- AHP26:替代半精度格式控制
- 异常使能位 IDE/IXE/UFE/OFE/DZE/IOE:默认全 0 = 屏蔽,不陷阱只记标志
改 FPCR 在多数核上是串行化操作(50--100 周期) ,只想改一次舍入就用
FRINT*指令,别MSR FPCR来回存恢复。
2. FPSR(Floating-point Status Register)
- 粘性标志:IOC / DZC / OFC / UFC / IXC / IDC
- 一旦置 1 不会自动清,软件写 0 才复位 → 长计算跑完统一
MRS X0, FPSR查一次即可 - AArch64 的
FCMP改的是 PSTATE.NZCV,不是 FPSR 的 N/Z/C/V(那几位是 AArch32 遗留)
生产代码几乎不开 FP 陷阱;glibc 的 fetestexcept() 就是读 FPSR。调试期开 IOC 陷阱能立刻抓 NaN 产生点,比让 quiet NaN 飘过整条流水线强。
三、搬移与转换:FMOV / FCVT
FMOV D0, D1 ; 浮点↔浮点,同宽
FMOV X0, D0 ; 位级搬运,不转换!原始比特
FMOV W1, S0
FCVT D0, S0 ; 单精度→双精度
FCVT S0, D0 ; 双→单(可能 inexact/underflow)
FCVT H0, S0 ; 单→半(FEAT_FP16)
整数↔浮点走 SCVTF/UCVTF(int→fp)和 FCVTZS/FCVTZU(fp→int,向零取整)。
⚠️ FMOV X0, D0 是比特搬运 ,不是 (int64)d0;要转整数必须 FCVTZS X0, D0。
四、算术:FADD/FSUB/FMUL/FDIV 与融合乘加
FADD S0, S1, S2
FSUB D0, D1, D2
FMUL S0, S1, S2
FDIV D0, D1, D2 ; 10--20+ 周期,贵
FSQRT D0, D1 ; 15--30+ 周期,更贵
FABS S0, S1
FNEG D0, D1
融合乘加(FMA 家族)------标量 FP 最重要的一组
FMADD D0, D1, D2, D3 ; D0 = D3 + D1*D2,只舍入一次
FMSUB S0, S1, S2, S3 ; S0 = S3 - S1*S2
FNMADD / FNMSUB ; 取负版
中间乘积无限精度不舍入 ,只有最后加完才舍一次 → 比 FMUL+FADD 快且准。Kahan 求和、补偿点积、double-double 全靠它。GCC/Clang 加 -ffp-contract=fast 会自动把 FMUL+FADD 融合成 FMADD。
五、快速倒数/平方根估计(DSP 热循环利器)
FRECPE S0, S1 ; 1 周期给 ~8 位精度 1/S1
FRSQRTE D0, D1 ; 1 周期给 1/sqrt(D1) 粗值
FRECPS S0, S1, S2 ; 一次牛顿迭代精化倒数
FRSQRTS D0, D1, D2 ; 一次牛顿迭代精化 rsqrt
单精度做 2 次 FRECPS 到 24 位尾数全精度,双精度 3 次;总延迟 ~5 周期 vs FDIV 15+,3× 加速,代价 1--2 ULP 误差。图形、物理、音频 DSP 大量用。
六、Min/Max 与 NaN:NM 变体才是日常该用的
| 指令 | NaN 行为 |
|---|---|
FMAX / FMIN |
任一操作数是 NaN → 回 NaN(毒化结果) |
FMAXNM / FMINNM |
跳过 quiet NaN,回另一个数(IEEE maxNum/minNum) |
FMAXNMV / FMINNMV |
NEON/SVE 归约版,同样跳 NaN |
扫数组求最大值时默认用 NM,否则一个哨兵 NaN 就把整轮结果毁了。
七、比较与无分支选择
FCMP D0, D1 ; 设 PSTATE.NZCV
FCMP S0, #0.0
FCCMP S0, S1, #nzcv, cond ; cond 真则比,假则把立即数灌进标志
FCSEL D0, D1, D2, eq ; eq 真选 D1 否则 D2
NaN 检测陷阱 :任一操作数 NaN → V=1,此时 B.EQ/B.GT/B.LT 全不跳。判 NaN 用自比较:
FCMP S0, S0
B.VS is_nan ; 非 NaN 永远等于自身,V=1 ⇔ NaN
FCCMP 能把 (a>0 && b<1) 这类复合条件压成两条无分支指令。
八、舍入指令 FRINT*(不碰 FPCR 的局部舍入)
FRINTRND / FRINTRI ; 按当前 RMode / 按整数舍入
FRINTP S0, S1 ; 向 +∞
FRINTM S0, S1 ; 向 −∞
FRINTZ S0, S1 ; 向零
FRINTA S0, S1 ; 向最近,ties away
比 MSR FPCR 存改恢复便宜得多,适合单点取整。
九、FP16 与 BF16:ML 降精度两条路
FEAT_FP16(IEEE binary16)
- 所有标准 FP 指令接受
Hn操作数:FADD H0,H1,H2 / FMADD H0,H1,H2,H3 - 同 128 位数据通路吞吐 2× FP32(8 个 FP16 vs 4 个 FP32)
- Apple M 系列、Cortex-A76+ 均支持
- 缺点:指数只有 5 位,>65504 就溢出,训练不稳
FEAT_BF16(bfloat16)
- 指数 8 位(同 FP32)→ 动态范围不缩水;尾数 7 位
- 关键指令:
BFMLALB/T、BFDOT、BFMMLA - AWS Graviton3(Neoverse V1)首发;PyTorch
torch.bfloat16经 ACL 后端直接映射到BFDOT/BFMMLA - BERT-base 推理 2.1× FP32 吞吐,GLUE 精度掉 <0.1%
选型直觉:推理激活值大、怕溢出 → BF16;纯移动端小模型、带宽极度敏感 → FP16。
十、ABI 坑:hard-float / soft-float 必须全工程一致
- hard-float:FP 参数走 S/D 寄存器,用真实 VFP 指令(AArch64 默认)
- soft-float:软件库模拟,参数走 X 寄存器
- softfp:用 FPU 但参数走整数寄存器(兼容旧 ABI)
混链 hard/soft 目标文件 → 链接错或诡异运行时 bug。AArch64 没有 soft-float 执行态,但工具链仍可 -mfloat-abi=soft 走软仿真。
十一、本文核心复盘
- V0--V31 是标量 FP / NEON / SVE 共用的 128 位文件;H/S/D 是低宽视图,写窄视图零扩展高位
- FPCR 管舍入(RMode)、FTZ(FZ)、默认 NaN(DN)、异常陷阱使能;FPSR 记粘性异常标志
- 改 FPCR 串行化,单点舍入用
FRINT* FMADD/FMSUB单次舍入,又快又准,编译器-ffp-contract=fast自动融合FDIV/FSQRT贵,热路径用FRECPE/FRSQRTE + FRECPS/FRSQRTS牛顿精化- 求 max/min 默认用
FMAXNM/FMINNM,避免 NaN 毒化 - 判 NaN 用
FCMP S0,S0 + B.VS;FCCMP/FCSEL做无分支复合条件 - FP16 吞吐翻倍但范围小;BF16 保范围降尾数,是服务器 ML 推理主力
- hard/soft-float ABI 全工程必须一致