Appendix A
指令集架构
-
机器语言的程序员:必须理解才能为计算机写出正确程序的计算机的结构
-
硬件设计者:必须理解才能为计算机设计合适的应用的机器描述
指令可被分成三类:
- 数据移动指令
- ALU指令
- 分支指令(控制流指令)
栈
优点:
- 代码密度高
- 硬件要求低
- 为栈架构写一个简单编译器很方便
缺点:
- 会变成瓶颈
- 不能并行或流水线
- 数据不一定总在栈顶,需要其它指令
- 很难为栈架构写一个优化的编译器
累加器
优点:
- 非常低的硬件要求
- 很容易设计和理解
缺点:
- 会成为瓶颈
- 不能并行或流水线
- 内存流量高
Mem-Mem
优点:
- 所需的指令很少(尤其对于3个操作数来说)
- 很容易写编译器(尤其对于3个操作数来说)
缺点:
- 很高的内存流量(尤其对于3个操作数来说)
- CPI高(尤其对于2个操作数来说)
- 2个操作数情况下需要数据移动
Mem-Reg
优点:
- 有些数据可以在load前就能被访问
- 指令格式易于编码
- 代码密度高
缺点:
- 操作数不对等
- CPI高
- 可能会限制Reg数量
Load-Store
优点:
- 指令编码简单、长度固定
- CPI相近
- 相对容易流水线
缺点:
- 指令数量多
- 不是所有的指令都需要3个操作数
- 依赖好的编译器
Reg
优点:
- 比cache快
- 是确定性的(没有missing)
- 可以复制(多个读端口)
- 标识符短(典型地,3-8比特)
- 降低了内存流量
缺点:
- 需要保存和恢复过程调用和上下文切换
- 不能取寄存器的地址(对于指针来说)
- 大小固定(不能高效存储string和structure)
- 编译器必须管理
一般使用寄存器的计算机和指令格式
- 是现在通用计算机的普遍选择
- 寄存器由小的“地址”标识(对于8-64个寄存器而言用3-6比特)
- load和store用一个长地址(1.5个短地址)和一个短地址
- 数学计算指令用三个短地址
指令集编码
- 想要有任意多的寄存器和寻址方式
- 寄存器和寻址模式字段的大小对平均指令大小的影响,因此对平均程序大小的影响
- 想要把指令编码成容易应用的长度
编译器和指令集
编译器的设计目标:
- 所有的程序都正确编译
- 大部分编译的程序运行速度快
- 大多数程序编译速度快
- 代码体积小
- 支持debug
多源编译器:同一个编译器可以编译不同语言
多目标编译器:同一个编译器可以生成在不同机器上运行的代码
基于编译器的寄存器优化:
- 假设寄存器数量少
- 优化靠编译器
- 高级语言程序没有显式引用寄存器
- 为每个候选变量分配符号或虚拟寄存器
- 将(无限个)符号寄存器映射到真实寄存器
- 不重叠的符号寄存器可以共享实际寄存器
- 如果实际寄存器用完了,有些变量可以用内存
变量的分配
栈:
- 用来分配局部变量
- 在过程调用和返回中增长或塌缩
- 堆栈分配的对象最适合用寄存器分配
全局数据区域:
- 用来分配全局变量和常数
- 很多这样的对象是数组和大型数据结构
- 不可能被分配去寄存器,除非它们是化名的(aliased)
堆:
- 用来分配动态对象
- 堆对象用指针访问
- 从不分配寄存器
设计指令集来提升编译性能
- 提供足够的通用寄存器,方便寄存器分配
- 通过保存指令、数据类型和寻址方式正交来提供常规指令集
- 提供原始构造而不是尝试对应一种高级语言
- 简化选择之间的tradeoff
- 允许编译器加速常用的例子
MIPS的数据类型:
- 字节
- 半字
- 字
- 双字