第2章:指令系统的设计
- 2.1 指令系统结构的分类
- 存储操作数的存储单元类型
- 指令系统的结构类型 ⭐
- 显式给出vs隐式给出
- 显式给出
- 隐式给出
- 堆栈型VS累加器型VS通用寄存器型
- 利用ALU指令的操作数的两个特征对通用寄存器结构进一步细分
- ALU指令的操作数个数
- ALU指令中存储器操作数的个数
- ALU指令中操作数个数和存储器操作数个数的典型组合
- 2.2 寻址方式
- 2.2.1 概念
- 2.2.2 一些操作数的寻址方式
- 说明
- 各种寻址方式举例
- 各种寻址方式的使用情况统计结果
- 两种表示寻址方式的方法
- 物理地址空间的信息如何存放?⭐
- 2.3 指令系统的设计和优化
- 2.3.1 指令系统的设计
- 2.3.2 对指令系统的基本要求 ⭐
- 2.3.3 指令系统的两种设计策略
- 2.3.4 控制指令 ⭐
- 2.3.5 指令操作码的优化
- 1. 哈夫曼编码 ⭐
- 2. 等长扩展码
- 3. 定长操作码
- 2.3.6 指令字格式的优化
- 2.4 指令系统的发展和改进
- 2.4.1 沿CISC方向发展和改进指令系统 ⭐
- 增强指令功能
- 2.4.2 沿RISC方向发展和改进指令系统 ⭐
- CISC指令系统结构存在的问题
- RISC机器遵循的原则
- 2.5 操作数的类型和大小
- 明确概念:
- 表示操作数类型的方法
- 操作数的大小
- 访问不同操作数大小的频率
- 2.6 MIPS指令系统结构
- MIPS的寄存器
- MIPS的数据表示
- 数据表示的类型
- 注意:
- 存储方式
- 寻址方式
- MIPS的指令格式 ⭐
- MIPS的操作
- 分类
- 符号的意义
- 指令举例
指令系统是计算机系统结构的主要内容,是软硬件交界面的主要部分。
2.1 指令系统结构的分类
区别不同指令系统结构的主要因素:CPU中用来存储操作数的数据单元类型
存储操作数的存储单元类型
- 堆栈
- 累加器
- 通用寄存器组
指令系统的结构类型 ⭐
- 堆栈结构
- 累加器结构
- 通用寄存器结构
根据操作数的来源不同,又可以进一步分为:
- 寄存器-存储器结构(RM结构)
- 操作数可以来自存储器
- 寄存器-寄存器结构(RR结构)
- 所有的操作数都是来自通用寄存器组
- 该结构也被称为load-store结构,只有load指令和store指令才能够访问存储器,其它指令无法直接从存储器中获得操作数,只能从通用寄存器组中获得操作数。
显式给出vs隐式给出
对于不同类型的结构,操作数的位置、个数以及操作数的给出方式(显式或隐式)也会不同
显式给出
用 指令字中的操作数字段
比如,直接给出操作数的值
隐式给出
使用事先约定好的单元
- 堆栈型结构中,操作数隐式给出,栈顶两个元素被弹出参与运算,计算结果被压入栈中
- 累加器型结构中,累加器内的操作数是隐式的,另一个操作数是显式给出的
比如:add X 其中累加器中的数据是隐式的,X是显式给出的。 - 在通用寄存器型结构中,所有的操作数都是显式给出的
比如:表达式在4种类型指令系统结构上的代码。假设:均保存在存储器单元中,并且不能破坏和的值。
由上面可以看出,堆栈型加法已经事先知道了操作数的位置,也就是栈顶前两个元素,所以加法指令就是ADD,因此,该操作数就是隐式给出。对其它类型的结构,分析也是如此
堆栈型VS累加器型VS通用寄存器型
- 堆栈型
- 优点:指令字比较短,程序占用的空间比较小
- 缺点:不能随机访问堆栈,难以生成有效的代码
- 累加器型
- 优点:指令字比较短,程序占用的空间比较小
- 缺点:因为只有一个中间结果暂存器(累加器),因此需要频繁的访问存储器,降低了效率
- 通用寄存器型----现代指令系统结构的主流
- 在灵活性和提高性能方面有明显的优势:
- 寄存器的访问速度比存储器快
- 对编译器而言,能更加容易、有效地分配和使用寄存器
- 寄存器可以用来存放变量从而可以:
- 减少对存储器的访问,加快程序的执行速度;
- 用更少的地址位来对寄存器进行寻址,从而减少程序的目标代码的大小。(利用寄存器地址代替存储器地址,找到数据所需要的地址信息更少)
利用ALU指令的操作数的两个特征对通用寄存器结构进一步细分
ALU指令的操作数个数
- 3个操作数的指令
两个源操作数和一个目标操作数 - 2个操作数的指令
其中一个操作数既可以作为源操作数也可以作为目的操作数
ALU指令中存储器操作数的个数
可以是0~3中的某一个,为0表示没有存储器操作数。
ALU指令中操作数个数和存储器操作数个数的典型组合
通用寄存器型结构进一步细分为3种类型
- 寄存器-寄存器型(RR型)
- 寄存器-存储器型(RM型)
- 存储器-存储器型(MM型)
优缺点对比:(m,n)表示指令的n个操作数中有m个存储器操作数
- 寄存器-寄存器型(RR型)
- 典型代表:RISC 精简指令集
- (0,3)
- 优点:指令字长固定,指令结构简洁,是一种简单的代码生成模型,各种指令的执行时钟周期数相近。
- 缺点:与指令中含存储器操作数的指令系统结构相比,指令条数多,目标代码不够紧凑,因而程序占用的空间比较大。
- 寄存器-存储器型(RM型)
- 典型代表:CISC 复杂指令集
- (1,2)
- 优点:可以在ALU指令中直接对存储器操作数进行引用,而不必先用load指令进行加载。容易对指令进行编码,目标代码比较紧凑。
- 缺点:一个操作数的内容将被改写,因此指令中的两个操作数不对称。
在一条指令中同时对寄存器操作数和存储器操作数进行编码,有可能限制指令所能够表示的寄存器个数。指令的执行时钟周期数因操作数的来源(R/M)不同而差别比较大。
- 存储器-存储器型(MM型)
- (2,2)/(3,3)
- 优点:目标代码最紧凑,不需要设置寄存器来保存变量。
- 缺点:指令字长变化很大,特别是3操作数指令。而且每条指令完成的工作也差别很大。对存储器的频繁访问会使存储器成为瓶颈。这种类型的指令系统结构现在已不用了。
2.2 寻址方式
2.2.1 概念
寻址方式:指令系统中如何形成要访问的数据的地址。
在通用寄存器型指令集结构中,一般是利用寻址方式指明指令中的操作数是一个常数、一个寄存器操作数、或者是一个存储器操作数。
- 寻址实际上是从形式地址到实际地址的转换。
- 形式地址由指令描述,实际地址也称为有效地址。
- 有效地址指明的是存储器单元的地址或寄存器地址。
2.2.2 一些操作数的寻址方式
说明
- :赋值操作
- Mem:存储器
- Regs:寄存器组
- 方括号:表示内容
例如:
- Mem[ ]:存储器的内容
- Regs[ ]:寄存器的内容
- Mem[Regs[R1]]:以寄存器R1中的内容作为地址的存储器单元中的内容
各种寻址方式举例
采用多种寻址方式可以显著地减少程序的指令条数,但可能增加计算机的实现复杂度以及指令的CPI。
各种寻址方式的使用情况统计结果
只需要注意:
- 立即数寻址方式和偏移寻址方式的使用频率最高
- 大约1/4的load指令和ALU指令采用了立即数寻址
两种表示寻址方式的方法
- 将寻址方式编码于操作码中,由操作码描述相应操作的寻址方式。
- 比如: 寻址方式只有很少的几种的结构:load-store结构
- 在指令字中设置专门的寻址字段,用以直接指出寻址方式。
- 处理机具有多种寻址方式,且指令有多个操作数。
物理地址空间的信息如何存放?⭐
信息宽度不超过主存宽度的信息必须存放在一个存储字内,不能跨边界。
也就是说:信息在主存中存放的起始地址必须是该信息宽度(字节数)的整数倍
虽然会存在存储空间的浪费,但是可以保证访问的速度
满足以下条件
- 字节信息的起始地址为:×…××××
- 半字信息的起始地址为:×…×××0
- 单字信息的起始地址为:×…××00
- 双字信息的起始地址为:×…×000
2.3 指令系统的设计和优化
2.3.1 指令系统的设计
首先考虑所应实现的基本功能,确定哪些基本功能应该由硬件实现,哪些功能由软件实现比较合适。
- 指令的功能设计
- 指令格式的设计
2.3.2 对指令系统的基本要求 ⭐
- 完整性
- 在一个有限可用的存储空间内,对于任何可解的问题,编制计算程序时,指令系统所提供的指令足够使用。要求指令系统功能齐全、使用方便
- 指令类型
- 规整性
- 对称性:所有与指令系统有关的存储单元的使用、操作码的设置等都是对称的。
- 均匀性:指对于各种不同的操作数类型、字长、操作种类和数据存储单元,指令的设置都要同等对待。
例如:如果某机器有5种数据表示,4种字长,2种存储单元,则要设置5×4×2=40种同一操作的指令。(一般情况下,都是实现有限的规整性,不会都去实现)
- 正交性
- 指令在编码时,应该是互相独立,互不相关的
- 高效率
- 指令执行速度快,使用频率高
- 兼容性
- 主要实现 向后兼容
2.3.3 指令系统的两种设计策略
- CISC(复杂指令系统计算机)
- 增强指令功能,把越来越多的功能交由硬件来实现,并且指令的数量也是越来越多。(但Intel在奔4之后就开始采取微内核架构,将内核设置为精简指令集)
- RISC(精简指令系统计算机)
- 尽可能地把指令系统简化,不仅指令的条数少,而且指令的功能也比较简单。
2.3.4 控制指令 ⭐
- 定义:控制指令是用来改变控制流的
- 跳转:当指令是无条件改变控制流时,称之为跳转指令。
- 分支:当控制指令是有条件改变控制流时,则称之为分支指令。
- 能够改变控制流的指令
- 分支(branch)
- 跳转(jump)
- 过程调用(call)
- 过程返回(return)
注意:改变控制流的大部分指令是分支指令(条件转移)
- 分支(branch)条件的方法及其优缺点
- 转移目标地址的表示
- 通常在指令中显示给出目标地址
- 过程返回指令不同,在编译时不知道返回地址(不知道程序被加载到哪里)。
- 所以,在指令中提供一个偏移量,由该偏移量和程序计数器(PC)的值相加而得出目标地址。(基于PC相对寻址)
- 优点:
有效地减少表示该目标地址所需要的位数(离PC近)
位置无关(代码可被装载到主存的任意位置执行) - 缺点:
模拟结果表明:采用4~8位的偏移量字段(以指令字为单位)就能表示大多数控制指令的转移目标地址了。
- 过程调用和返回
- 改变控制流,保存机器状态,保存返回地址
- 过去有些指令系统结构提供了专门的保存机制来保存许多寄存器的内容
- 现在较新的指令系统结构则要求由编译器生成load和store指令来保存或恢复寄存器的内容。
2.3.5 指令操作码的优化
指令格式的设计:确定指令字的编码方式,包括操作码字段和地址码字段的编码和表示方式。
指令格式的优化:如何用最短的位数来表示指令的操作信息和地址信息。
1. 哈夫曼编码 ⭐
- 基本思想
- 当各种事件发生的概率不均等时,可以对发生概率最高的事件用最短的位数(时间)来表示(处理),而对于出现概率较低的事件,则可以用较长的位数(时间)来表示(处理),从而使总的平均位数(时间)缩短。
- 构造哈夫曼树的方法
- 将各事件按其使用频度从小到大依次排列
- 每次从中选择两个频度值最小的结点,将其合并成一个新的结点,并把新结点画在所选结点的上面,然后用两条边把新结点分别与那两个结点相连。(注意:新结点的频度值是所选两个结点的频度值的和)
- 把新结点与其他剩余未结合的结点一起,再以上面的步骤进行处理,反复进行,直到全部结点都结合完毕、形成根结点为止。
- 信息熵
- 用来描述操作码的优化程度,越小越好。
- 也就是说,当信息熵越大时,表示所含信息混乱程度就越大,为了使得用最短的位数表示指令的操作信息和地址信息,所以熵值越小越好
- 代码的优化程度:H
- 其中H表示为信息熵,p为每个指令的概率,n为指令总数。
- 也可以表示为:理论上的编码最短码长
- 有关习题
- 例题:
- 解题步骤:
- 约定编码方法:左边是1 右边是0
- 哈夫曼编码的优缺点
- 优点:可以减少操作码的平均位数
- 缺点:所获得的编码是变长度的,不规整,不利于硬件处理。
- 扩展操作码
- 位于定长二进制编码和哈夫曼编码之间的一种编码方案
- 扩展操作码:
- 这种方法的出来的结果要比哈夫曼编码大,但是编码长度较为工整,而且比定长的三位编码要小很多。(定长3位:3//哈夫曼编码:2.20//扩展操作码:2.30)
2. 等长扩展码
为了便于分级译码,一般都采用等长扩展码。
选用哪种编码法取决于指令使用频度pi的分布。
- 15/15/15法
若在头15种指令中pi 的值都比较大,但在后30种指令后急剧减少,则应选择15/15/15法;
- 8/64/512法
若pi的值在头8种指令中较大,之后的64种指令的pi值也不太低,则应选择8/64/512法。
衡量标准:看哪种编码法能使平均码长最短。
3. 定长操作码
固定长度的操作码:所有指令的操作码都是同一的长度(如8位)。
许多计算机都采用(特别是 RISC 精简指令集计算机
以程序的存储空间为代价来换取硬件实现上的好处。
2.3.6 指令字格式的优化
存在的问题:
如果指令字的宽度固定,地址码的长度和个数固定,则操作码的缩短并不能带来好处,只是使指令字中出现空白浪费。
采用地址个数可变和/或地址码长度可变的方案
最常用的操作码最短,其地址字段个数最多。
能够使指令的功能增强,从总体上减少所需的指令条数。
寄存器的个数以及寻址方式对指令字长也有很大的影响
因此,在指令系统进行设计的过程中,要在指令字中选择合适的指令字长和寄存器的个数。
-------->指令系统的三种编码格式(在操作码长度固定的情况下) ⭐
- 可变长度编码
- 当指令系统的寻址方式和操作种类很多时,这种编码格式是最好的。
- 用最少的二进制位来表示目标代码。
- 可能会使各条指令的字长和执行时间相差很大
- 比如:
- 固定长度编码格式
- 将操作类型和寻址方式一起编码到操作码中
- 当寻址方式和操作类型非常少时,这种编码格式非常好。
- 可以有效地降低译码的复杂度,提高译码的速度。
- 大部分 RISC的指令系统
- 混合型编码格式
- 提供若干种固定的指令字长。
- 以期达到既能够减少目标代码长度又能降低译码复杂度的目标。
- 例如:
2.4 指令系统的发展和改进
2.4.1 沿CISC方向发展和改进指令系统 ⭐
CISC指令系统的特点:指令数量多,功能多样
增强指令功能
- 面向目标程序增强指令功能----根据应用优化
- 对大量的目标程序及其执行情况进行统计分析,找出那些使用频度高、执行时间长的指令或指令串。
- 对于使用频度高的指令,用硬件加快其执行;
- 对于使用频度高的指令串,用一条新的指令来替代。
- 改进方面:
- 增强运算型指令的功能----处理
- 增强数据传送指令的功能----I/O
- 增强程序可控制指令的功能----控制
- 举例:
循环在程序中占有相当大的比例,所以可以在指令设计上提供专门的支持
- 面向高级语言的优化实现来改进指令系统----编译过程优化
- 缩小高级语言与机器语言的语义差距
- 高级语言与一般的机器语言的语义差距非常大,为高级语言程序的编译带来了一些问题。
- 增强对高级语言和编译器的支持
- 对高级语言中使用频度高、执行时间长的语句,增强有关指令的功能,加快这些指令的执行速度
- 增加专门的指令,可以达到减少目标程序的执行时间和减少目标程序长度的目的。
- 增强系统结构的规整性,减少系统结构中的各种例外情况。
- 高级语言计算机(一种比较激进的方法)
- 间接执行高级语言机器
- 高级语言作为机器的汇编语言。
- 直接执行高级语言的机器
- 直接把高级语言作为机器语言。
- 比较简单的系统结构+软件
- 能够在较低成本和复杂度的前提下,提供更高的性能和灵活性。
- 面向操作系统的优化实现改进指令系统
操作系统和计算机系统结构是紧密联系的,操作系统的实现在很大程度上取决于系统结构的支持。
- 指令系统对操作系统的支持主要有:
- 处理机工作状态和访问方式的切换;
- 进程的管理和切换;
- 存储管理和信息保护;
- 进程的同步与互斥,信号灯的管理等。
支持操作系统的有些指令属于特权指令,一般用户程序是不能使用的。
2.4.2 沿RISC方向发展和改进指令系统 ⭐
CISC指令系统结构存在的问题
- 各种指令的使用频率相差悬殊,许多指令很少使用
使用频度高的指令也是最简单的指令。
- 指令系统庞大,指令条数很多,许多指令的功能又很复杂,使得控制器硬件非常复杂。
- 许多指令由于操作繁杂,其CPI值比较大,执行速度慢。采用这些复杂指令有可能使整个程序的执行时间反而增加。
- 由于指令功能复杂,规整性不好,不利于采用流水技术来提高性能。
RISC机器遵循的原则
- 指令条数少、指令功能简单
- 只选取使用频度很高的指令,在此基础上补充一些最有用的指令
- 采用简单而又统一的指令格式,并减少寻址方式;
- 指令字长都为32位或64位
- 指令的执行在单个机器周期内完成;
- (采用流水线机制)
- 只有load和store指令才能访问存储器
- 其它指令的操作都是在寄存器之间进行
- 采用load-store结构
- 大多数指令都采用硬连逻辑来实现
- 强调优化编译器的作用,为高级语言程序生成优化的代码
- 充分利用流水技术来提高性能。
2.5 操作数的类型和大小
明确概念:
- 数据表示:计算机硬件能够直接识别,指令系统可以直接调用的数据类型
- 数据结构:由软件进行处理和实现的各种数据类型
系统结构设计者要解决的问题: 如何确定数据表示
表示操作数类型的方法
- 由指令中的操作码指定操作数的类型
- 给数据加上标识,由数据本身给出操作数类型(采用该方案的机器很少见)
操作数的大小
操作数的位数或字节数
主要大小:字节、半字、字、双字
- 字符:用ASCII码表示,一个字节
- 整数:用二进制补码表示,一个字节、半字、单字
- 浮点数:单精度浮点数----单字、双精度浮点数----双字
- 一般都采用IEEE 754浮点标准
- 十进制操作数类型
- 压缩十进制或二进制编码十进制(BCD码)
- 用4位二进制编码表示数字0~9,并将两个十进制数字合并到一个字节中存储。
- 非压缩十进制
- 将十进制数直接用字符串来表示。
访问不同操作数大小的频率
spec基准程序对于单字和双字的数据访问具有较高的频度
一台32位的机器应该支持8、16、32位整型操作数以及32位和64位的IEEE 754标准的浮点操作数。
2.6 MIPS指令系统结构
“无内部互锁流水级的微处理器”(Microprocessor without interlocked piped stages)
MIPS的寄存器
- 32个64位通用寄存器
- R0,R1,…,R31
- 也称为整数寄存器
- R0的值永远是0
- 32个64位浮点数寄存器
- F0,F1,…,F31
- 用来存放32个单精度浮点数(32位),也可以用来存放32个双精度浮点数(64位)。
- 存储单精度浮点数(32位)时,只用到FPR的一半,其另一半没用。
- 一些特殊的寄存器
- 它们可以与通用寄存器交换数据
- 例如浮点状态寄存器:用来保存有关浮点操作结果的信息。
MIPS的数据表示
数据表示的类型
- 整数
- 字节
- 半字
- 字
- 双字
- 浮点数
- 单精度浮点数32位
- 双精度浮点数64位
注意:
字节、半字或者字在装入64位寄存器时,用零扩展或者用符号位扩展来填充该寄存器的剩余部分。
装入以后,对它们将按照64位整数的方式进行运算。
存储方式
- 大端存储
- 小端存储
寻址方式
- 立即数寻址
- 寄存器寻址
- 偏移量寻址
- 地址 = 立即数 + 偏移量
- 立即数字段和偏移量字段都是16位的
- 寻址方式是编写到操作码中的
- MIPS的存储器是按字节寻址,地址为64位
- 所有存储器的访问都必须是边界对齐的
MIPS的指令格式 ⭐
寻址方式编码到操作码中,同时所有的指令都是32位的,其中操作码占6位
分为3种指令格式:在3种指令格式中,同名字段的位置固定不变
- I类指令(指示类)
- 包括所有的load和store指令,立即数指令,分支指令,寄存器跳转指令,寄存器链接跳转指令
- 立即数字段为16位,用于提供立即数或偏移量
- 比如:
- 操作码:用于指定指令的操作类型(但没有funct域)
- rs:指定第一个源操作数所在的寄存器编号
- rt,指定用于目的操作数(保存运算结果)的寄存器编号
- Immediate:16-bit的立即数
- 举例:
- R类指令(运行类)
- 包括ALU指令,专用寄存器读写指令,move指令等
- ALU指令
- 比如:
- 操作码:用于指定指令的类型。对于R型指令,该域的值均为0
- rs:第一个源操作数所在的寄存器编号;
- rt:第二个源操作数所在的寄存器编号;
- rd:目的操作数(保存运算结果)的寄存器编号;
- sham:移位指令进行移位操作的位数。
- funct:具体的运算操作编码,与操作码域组合,精确地指定指令的类型。
- J类指令
- 包括跳转指令,跳转并链接指令,自陷指令,异常返回指令。
- 在这类指令中,指令字的低26位是偏移量,它与PC值相加形成跳转的地址。
- 比如:
MIPS的操作
分类
- load和store
- ALU操作
- 分支与跳转
- 浮点操作
符号的意义
- 下标:表示字段中具体的位;
- 对于指令和数据,按从最高位到最低位(即从左到右)的顺序依次进行编号,最高位为第0位,次高位为第1位,依此类推。
- 下标可以是一个数字,也可以是一个范围。
- Mem :表示主存;
按字节寻址,可以传输任意个字节。 - 上标:用于表示对字段进行复制的次数。
- 符号##:用于两个字段的拼接,并且可以出现在数据传送的任何一边。
指令举例
- load和store指令
- ALU指令
- 寄存器-寄存器型(RR型)指令或立即数型
- 算术和逻辑操作:加、减、与、或、异或和移位等
- 注意:R0的值永远是0,它可以用来合成一些常用的操作
- 控制指令
- 跳转:无条件跳转
- 4种:跳转、跳转并链接、间接跳转、间接跳转并链接跳转
- 目标地址由26位偏移量左移两位与PC相加间接跳转目标地址由指定寄存器给出的.
- 分支:条件转移
- 分支条件由指令确定
- 分支的目标地址由16位带符号偏移量左移两位后与pc相加
- 举例:
- MIPS的浮点操作
- 由操作码指出操作数是单精度(SP)或是双精度(DP)
- 后缀S:表示操作数是单精度浮点数
- 后缀D:表示是双精度浮点数
- 浮点操作
- 包括:加减乘除分别有单精度和双精度指令
- 浮点数比较指令
根据比较结果设置浮点状态寄存器中的某一位,以便于后面的分支指令BC1T(若真则分支)或BC1F(若假则分支)测试该位,以决定是否进行分支。