核心是加法器(补码把减法变加法),沿「全加器→多位加法器→ALU→移位→乘除→标志位」推进。
一位加法三个输入(加数位 a、b,低位进位 cin)、两个输出(和 s、进位 cout):
s = a ⊕ b ⊕ cin
cout = (a ∧ b) ∨ (cin ∧ (a ⊕ b)) // 至少两个 1 就产生进位
半加器只处理 a、b 无低位进位;n 个全加器串联(cin 接上一级 cout)= n 位行波进位加法器(ripple-carry adder)。
行波进位:进位从最低位逐级滚到最高位,第 i 位要等第 i-1 位进位。延迟 = n × 单级延迟,O(n);结构简单面积小,位数一大即成关键路径(第 1 章)。
超前进位(CLA):并行预计算所有进位。定义两个信号:
则 cᵢ₊₁ = gᵢ ∨ (pᵢ ∧ cᵢ),展开:
c1 = g0 ∨ (p0 ∧ c0)
c2 = g1 ∨ (p1 ∧ g0) ∨ (p1 ∧ p0 ∧ c0)
c3 = g2 ∨ (p2 ∧ g1) ∨ (p2 ∧ p1 ∧ g0) ∨ (p2 ∧ p1 ∧ p0 ∧ c0)
...
所有进位只依赖 g、p、c0,一层门同时算出,延迟 O(log n)。
对比:CLA 用「额外电路+高扇入」换延迟。工程折中是分组 CLA:组内 CLA、组间串行(或两级 CLA),不大面积爆炸又压低延迟。
| 加法器 | 延迟 | 面积/扇入 | 适用 |
|---|---|---|---|
| 行波进位 | O(n) | 最小 | 面积优先、位数短 |
| 全 CLA | O(log n) | 大、高扇入 | 延迟优先 |
| 分组 CLA | 介于两者 | 折中 | 主流 |
ALU 在加法器外圈加选择逻辑,用功能选择线切出多种运算:
┌───────────┐
A ───▶│ │
B ───▶│ ALU │──▶ 结果 F
op ──▶│ │──▶ 标志位 ZF/OF/CF/SF
└───────────┘
n 位 ALU 至少支持:加、减(A+补码(B))、按位与/或/异或、非、比较(减但不写回、只置标志)、移位。减法 = B 取反 + cin=1:反相器取反 B、最低位进位置 1,恰等于加 B 的补码。
| 移位 | 操作 | 空位补 | 用途 |
|---|---|---|---|
| 逻辑左移 SLL | 整体左移 | 补 0 | x << 1 = 乘 2 |
| 逻辑右移 SRL | 整体右移 | 补 0 | unsigned 除 2 |
| 算术右移 SRA | 右移 | 补符号位 | signed 除 2(保符号) |
| 循环移位 ROL/ROR | 移出的位补到另一端 | 自身 | 位旋转、密码学 |
注意:算术右移 vs 逻辑右移 = C 里
int >> 1与unsigned >> 1的机器级区别。有符号右移是「实现定义」,主流编译器对负数用算术右移(-5 >> 1 = -3);无符号一律逻辑右移;左移对负数溢出是 UB(第 2 章)。
移位器是组合逻辑,用桶形移位器一次移任意位;乘法里用移位+加法替代乘常数(x*10 = (x<<3)+(x<<1))。
乘法 = 移位+累加:按乘数每位决定加被乘数或加 0,再左移一位:
1101 (13)
× 1011 (11)
─────────────
1101 ← 乘数最低位为 1,加
1101 ← 左移 1 位再加
0000 ← 该位为 0,加 0
1101 ← 左移 3 位再加
─────────────
10001111 (143)
硬件用 Booth 算法处理有符号乘法:对补码同样有效,遇连续 1(…01110…)可一次跳过、减少加减次数。现代 CPU 用阵列乘法器或华莱士树并行压缩部分积,单周期或几周期出结果。
除法 = 移位+比较+减。恢复余数法:试减除数,够减商 1,不够商 0 并恢复(加回除数);不恢复余数法省掉恢复一步,用符号控制下一步加减,是硬件主流。
对比:乘除比加减慢得多——整数加法 1 周期、乘法 3
4 周期、除法 2090 周期(x86div极慢)。故编译器把「除以常数」优化成「乘倒数+移位」(x/10 ≈ (x × 0xCCCCCCCD) >> 35),避免真除法。
ALU 运算后置一组条件码标志位(x86 的 EFLAGS),供条件跳转读取:
| 标志 | 含义 | 置位条件 |
|---|---|---|
| ZF | 零标志 | 结果为 0 |
| SF | 符号标志 | 结果最高位(符号位)为 1 |
| CF | 进位/借位标志 | 无符号运算产生进位或借位(C_out) |
| OF | 溢出标志 | 有符号运算溢出(第 2.3 节 C_out ⊕ C_in) |
注意:CF/OF 分别服务无符号/有符号——
1111 1111 + 0000 0001作无符号是 255+1=256 进位(CF=1),作有符号是 -1+1=0 不溢出(OF=0)。CPU 同时置两个,由指令语义决定看哪个:ja/jb看 CF,jg/jl看 OF+SF——故 C 的unsigned/signed比较编译出不同跳转。
x += y 可能是 add(改标志)也可能被优化成 lea(不碰标志);a < b 对 int/unsigned 生成不同跳转——根子在 3.6 的 CF/OF。