复杂指令系统计算机(CISC)
CISC 不是”设计得不好的 ISA”,它是在特定资源约束下的合理解。要看懂 4.4,得先看懂它当年在优化什么——因为 RISC 之所以能赢,正是那三条约束全部消失了。
机制
三条历史约束
| 约束(1970 年代) | 导致的设计选择 |
|---|---|
| 主存极贵、极小(以 KB 计) | 代码密度压倒一切——能少一个字节是一个字节 |
| 编译器不成熟 | 希望硬件提供接近高级语言语义的指令,缩小”语义鸿沟”(如一条指令完成数组访问、循环、串比较) |
| 微程序控制器普及 | 加一条复杂指令的边际成本极低——只要写一段微程序,不必改硬件 |
第三条是关键的加速器。 在微程序控制器(5.4 控制器(本地资料))下,指令集扩充变成了”往控制存储器里多烧一段微码”,几乎免费。于是指令集只增不减,几百条是常态。
典型特征
| 维度 | CISC 的做法 |
|---|---|
| 指令数量 | 多,数百条 |
| 指令长度 | 可变(x86:1~15 字节) |
| 指令格式 | 多种,字段位置不固定 |
| 寻址方式 | 多,十几种 |
| 访存 | 几乎任何指令都能直接访存 |
| 指令周期 | 不等长,多数指令需多个时钟周期 |
| 通用寄存器 | 少(IA-32 只有 8 个) |
| 控制器 | 典型搭配微程序控制 |
| 优化重心 | 硬件(复杂指令由硬件/微码实现) |
“几乎任何指令都能访存”这一条最要紧,它是 CISC 与 RISC 最实质的分界,后果会在 4.4.2 展开。
复杂指令值不值:本章已经给过一正一反两个例证
关联对照:
lea和loop,同样是复杂指令,一个赢一个输
二八定律:RISC 论证的实测依据
1970 年代末的统计发现:
程序 80% 的运行时间,只用到了大约 20% 的指令。
换句话说,指令集里那 80% 的复杂指令,只贡献了 20% 的执行时间,却让译码器、控制器、流水线全部为它们付出复杂度代价。
这条统计结论是 RISC 的直接触发点——它把”复杂指令好不好”从哲学争论变成了一个可测量的工程问题。
变长指令的译码代价
4.1.4 立下的主线在这里结算:变长指令省存储空间,代价是取指与译码的串行依赖。
x86 把这个代价推到了极致——一条指令由若干前缀字节 + 操作码 + ModRM 字节 + SIB 字节 + 位移 + 立即数拼成,每一段的存在与否都由前一段决定:
graph LR A["取第 1 字节"] --> B{"是前缀吗?"} B -->|是| A B -->|否| C["这是操作码"] C --> D{"需要 ModRM 吗?"} D -->|是| E["取 ModRM"] --> F{"需要 SIB 吗?"} D -->|否| G F -->|是| H["取 SIB"] --> G F -->|否| G G["取位移/立即数"] --> I["★ 到此才知道指令有多长"] classDef hot fill:#f8cecc,stroke:#b85450,stroke-width:2px class I hot
要知道第 2 条指令从哪开始,必须先把第 1 条译到最后。 这在单发射机器上只是慢一点,在超标量机器上是致命的——一拍要发射 4 条指令,就得先切分 4 条指令的边界。现代 x86 为此专门加了长度预译码级和指令边界标记缓存,这些电路 RISC 完全不需要。
现代 x86 的内部 RISC 化
x86 至今仍是 CISC ISA,但它的微体系结构早已是 RISC 式的:
这是 4.1.1 那张三层图最有力的一个例证:ISA 层必须保持 CISC(否则几十年的软件全部作废),微体系结构层可以完全换成 RISC 风格。“契约不变,实现随便换”——ISA 存在的意义在这里体现得最彻底。
代价也很清楚:那个译码器就是 x86 为向后兼容付出的常驻硬件成本,它一直在那儿耗电、占面积,无论跑什么程序。
边界
“CISC 用微程序控制器”是典型搭配,不是定义。 CISC 的定义在指令集的复杂度,不在控制器的实现方式。现代 x86 处理器对简单指令用硬布线快速译码、只对复杂指令走微码——两种控制方式在同一颗芯片里共存。判断题若说”CISC 必须采用微程序控制器”,错。
指令条数多不是 CISC 的本质,“单条指令功能复杂 + 任意指令可访存”才是。 现代 RISC-V 加上各种扩展后指令条数并不少,但它仍是 RISC,因为每条指令依然简单、依然只有 load/store 访存。
代码密度高是 CISC 至今没有被淘汰的一个真实理由。 指令占的空间小 → 同样大小的指令 Cache 能装下更多指令 → 指令 Cache 命中率更高(3.5.2)。在存储器已经不贵的今天,这条理由从”省内存”变成了”省 Cache”,但它依然成立。
CISC 的复杂指令并非都没用。 串操作指令(REP MOVSB)在现代 x86 上被专门优化过,拷贝大块内存时比手写循环还快——又是”硬件有没有优待它”这条判据。
“语义鸿沟”这个动机今天基本失效了。 编译器的优化能力早已超过手写汇编,用多条简单指令合成的代码往往比一条复杂指令更快(因为调度空间更大)。这是三条历史约束里消失得最彻底的一条。
对照速查
| 维度 | CISC |
|---|---|
| 指令数量 | 多(数百条) |
| 指令长度 | 可变 |
| 寻址方式 | 多(十几种) |
| 访存 | 任意指令均可 |
| 指令周期 | 不等长、多周期 |
| 通用寄存器 | 少 |
| 控制器 | 典型微程序(非定义) |
| 优化重心 | 硬件 |
| 代码密度 | 高 |
| 判断 | 对错 |
|---|---|
| CISC 必须用微程序控制器 | ❌(典型搭配,非定义) |
| CISC 的本质是指令条数多 | ❌(是单条功能复杂 + 任意指令可访存) |
| CISC 代码密度高 | ✅ |
| 代码密度高对 Cache 有利 | ✅ |
| 二八定律说 80% 的指令占 20% 的时间 | ⚠️ 反着说也对:20% 的指令占 80% 的时间 |
| 现代 x86 已经不是 CISC | ❌(ISA 仍是 CISC,微架构是 RISC 式) |
| 复杂指令一定比等价的简单指令序列快 | ❌(loop 就是反例) |
考点
- CISC 的三条历史动因:主存贵(代码密度)、编译器弱(语义鸿沟)、微程序使扩充廉价
- 特征:指令多、变长、寻址方式多、任意指令可访存、多周期、寄存器少、典型配微程序
- “任意指令可访存”是与 RISC 最实质的分界
- 二八定律:20% 的指令占 80% 的运行时间 → RISC 的实测依据
- 变长指令的代价是译码串行,超标量下必须加长度预译码
- 现代 x86:ISA 是 CISC,微体系结构是 RISC 式(译码成 μops)——ISA 三层的最佳例证
- 判据:复杂指令值不值,看硬件有没有为它建专用通路(
lea赢、loop输) - 微程序控制是典型搭配不是定义
链接
- 🏠 返回总览:计算机组成原理第 4 章:指令系统总览
- ⬅️ 上一节:4.3.4 过程调用的机器级表示
- ➡️ 下一节:4.4.2 精简指令系统计算机(RISC)
- 🔗 4.1.4 扩展操作码指令格式(变长指令的译码代价)
- 🔗 4.1.1 指令集体系结构(ISA 与微体系结构分层)
- 🔗 5.4 控制器的功能和工作原理(本地资料)(微程序控制)
- 📖 名词库:第 4 章名词库