第2章 指令系统的设计
2.4 指令系统的发展和改进
2.4.1 沿CISC方向发展和改进指令系统
CISC指令系统的一大特点
- 指令数量多、功能多样
增强指令功能主要是从以下3个方面着手:
- 面向目标程序增强指令功能
- 面向高级语言的优化实现来改进指令系统
- 面向操作系统的优化实现改进指令系统
2.4.1.1 面向目标程序增强指令功能
- 对大量的目标程序及其执行情况进行统计分析,找出那些使用频度高、执行时间长的指令或指令串。
- 对于使用频度高的指令,用硬件加快其执行;
- 对于使用频度高的指令串,用一条新的指令来替代。
- 既能减少目标程序的执行时间,也能有效地缩短程序的长度。
- 可以从以下几个方面来改进:
- 增强运算型指令的功能
- 增强数据传送指令的功能
- 增强程序控制指令的功能
举例:
循环控制部分通常用3条指令完成:一条加法指令、一条比较指令、一条分支指令
设置循环控制指令,用一条指令完成上述3条指令的功能。
2.4.1.2 面向高级语言的优化实现来改进指令系统(缩小高级语言与机器语言的语义差距)
-
高级语言与一般的机器语言的语义差距非常大,为高级语言程序的编译带来了一些问题。
- 编译器本身比较复杂;
- 编译生成的目标代码比较难以达到很好的优化。
-
增强对高级语言和编译器的支持 (面向高级语言的计算机 )
- 对高级语言中使用频度高、执行时间长的语句,增强有关指令的功能,加快这些指令的执行速度,或者增加专门的指令,可以达到减少目标程序的执行时间和减少目标程序长度的目的。
- 增强系统结构的规整性,减少系统结构中的各种例外情况。
-
高级语言计算机
- 间接执行高级语言机器
- 高级语言作为机器的汇编语言。
- 直接执行高级语言的机器
- 直接把高级语言作为机器语言。(一种比较激进的方法 )
- 间接执行高级语言机器
-
采用“比较简单的系统结构+软件”的做法能够在较低成本和复杂度的前提下,提供更高的性能和灵活性。
2.4.1.3 面向操作系统的优化实现改进指令系统
- 操作系统和计算机系统结构是紧密联系的,操作系统的实现在很大程度上取决于系统结构的支持。
- 指令系统对操作系统的支持主要有:
- 处理机工作状态和访问方式的切换;
- 进程的管理和切换;
- 存储管理和信息保护;
- 进程的同步与互斥,信号灯的管理等。
- 支持操作系统的有些指令属于特权指令,一般用户程序是不能使用的。
2.4.1.4 CISC存在的问题
- 20%与80%规律
- VLSI技术的发展引起的问题
- 软硬件功能分配问题
- 这些问题导致RISC体系结构的产生
2.4.2 沿RISC方向发展和改进指令系统
2.4.2.1 CISC指令系统结构存在的问题
各种指令的使用频度相差悬殊,许多指令很少用。 (二八定律)
- 据统计:只有20%的指令使用频度比较高,占运行时间的80%。而其余80%的指令只在20%的运行时
间内才会用到。 - 使用频度高的指令也是最简单的指令。
分析:
- 指令系统庞大,指令条数很多,许多指令的功能又很复杂,使得控制器硬件非常复杂。
导致的问题:
- 占用了大量的芯片面积(如占用CPU芯片总面积的一半以上),给VLSI设计造成很大的困难;
- 增加了研制时间和成本,容易造成设计错误。
-
许多指令由于操作繁杂,其CPI值比较大,执行速度慢。采用这些复杂指令有可能使整个程序的 执行时间反而增加。
-
由于指令功能复杂,规整性不好,不利于采用流水技术来提高性能。
2.4.2.2 设计RISC机器遵循的原则
- 指令条数少、指令功能简单。只选取使用频度很高的指令,在此基础上补充一些最有用的指令;
- 采用简单而又统一的指令格式,并减少寻址方式;指令字长都为
32
位或64
位; - 指令的执行在单个机器周期内完成;(采用流水线机制)
- 只有
load
和store
指令才能访问存储器,其它指令的操作都是在寄存器之间进行;(即采用load-store
结构) - 大多数指令都采用硬连逻辑来实现;
- 强调优化编译器的作用,为高级语言程序生成优化的代码;
- 充分利用流水技术来提高性能。
2.5 操作数的类型和大小
数据表示:计算机硬件能够直接识别、指令系统可以直接调用的数据类型。
- 所有数据类型中最常用、相对比较简单、用硬件实现比较容易的几种。
数据结构:由软件进行处理和实现的各种数据类型。
- 研究:这些数据类型的逻辑结构与物理结构之间的关系,并给出相应的算法。
系统结构设计者要解决的问题:如何确定数据表示?(软硬件取舍折中的问题)
表示操作数类型的方法有两种
- 由指令中的操作码指定操作数的类型。
- 给数据加上标识,由数据本身给出操作数类型。
- 优点:简化指令系统,可由硬件自动实现一致性检查和类型转换,缩小了机器语言与高级语言的语义差距,简化编译器等。
- 缺点:由于需要在执行过程中动态检测标志符,动态开销比较大,所以采用这种方案的机器很少见。
操作数的大小: 操作数的位数或字节数。
- 主要的大小:字节(8位)、半字(16位)、字(32位)、和双字(64位)
几种常见的操作数类型
- 字符:用ASCII码表示,为一个字节大小。
- 整数:用二进制补码表示,其大小可以是字节、半字或单字。
- 浮点操作数:单精度浮点数(1个字)、双精度浮点数(双字)。一般都采用IEEE 754浮点标准
- 十进制操作数类型
- 压缩十进制或二进制编码十进制(BCD码):用4位二进制编码表示数字0~9,并将两个十进制数字合并到一个字节中存储。
- 非压缩十进制:将十进制数直接用字符串来表示。
2.6 MIPS指令系统结构
2.6.1 MIPS的寄存器
- 32个64位通用寄存器(GPRs)
- R0,R1,…,R31,也称为整数寄存器
- R0的值永远是0
- 32个64位浮点数寄存器(FPRs)
- F0,F1,…,F31
- 用来存放32个单精度浮点数(32位),也可以用来存放32个双精度浮点数(64位)。
- 存储单精度浮点数(32位)时,只用到FPR的一半,其另一半没用。
- 一些特殊寄存器
- 它们可以与通用寄存器交换数据。
- 例如浮点状态寄存器:用来保存有关浮点操作结果的信息。
2.6.2 MIPS的数据表示
- MIPS的数据表示
- 整数:字节(8位) 、半字(16位)、字(32位)、双字(64位)
- 浮点数:单精度浮点数(32位)、双精度浮点数(64位)
- 字节、半字或者字在装入64位寄存器时,用零扩展或者用符号位扩展来填充该寄存器的剩余部分。装入以后,对它们将按照64位整数的方式进行运算。
2.6.3 MIPS的数据寻址方式
- 立即数寻址与偏移量寻址;
- 立即数字段和偏移量字段都是
16
位的。
- 立即数字段和偏移量字段都是
- 寄存器间接寻址是通过把
0
作为偏移量来实现的; - 16位绝对寻址是通过把
R0
(其值永远为0)作为基址寄存器来完成的; - MIPS的存储器是按字节寻址的,地址为
64
位; - 所有存储器访问都必须是边界对齐的。
2.6.4 MIPS的指令格式
- 寻址方式编码到操作码中
- 所有的指令都是32位的
- 操作码占6位
- 3种指令格式:3种格式中,同名字段的位置固定不变。
2.6.4.1 I类指令
-
包括所有的load和store指令,立即数指令,分支指令,寄存器跳转指令,寄存器链接跳转指令。
-
立即数字段为16位,用于提供立即数或偏移量。
指令 | 操作 | 说明 |
---|---|---|
load指令 | Regs[rs]+immediate | 从存储器取来的数据放入寄存器rt |
store指令 | Regs[rs]+immediate | 要存入存储器的数据放在寄存器rt中 |
立即数指令 | Regs[rt] ← Regs[rs] op immediate | |
分支指令 | Regs[rs]+immediate,rt无用 | 转移目标地址 |
寄存器跳转 寄存器跳转并链接 | Regs[rs] | 转移目标地址为Regs[rs] |
2.6.4.2 R类指令
- 包括ALU指令,专用寄存器读/写指令,move指令等。
指令 | 操作 | 说明 |
---|---|---|
ALU指令 | Regs[rd]← Regs[rs] funct Regs[rt] | funct为具体的运算操作编码 |
2.6.4.3 J类指令
- 包括跳转指令,跳转并链接指令,自陷指令,异常返回指令。
- 在这类指令中,指令字的低
26
位是偏移量,它与PC
值相加
形成跳转的地址。
2.6.5 MIPS的操作
2.6.5.1 MIPS指令可以分为四大类
- load和store
- ALU操作
- 分支与跳转
- 浮点操作
2.6.5.2 符号的意义
- 传送符号
符号 | 意义 |
---|---|
x ← n y x←_{n}y x←ny | 从y传送n位到x |
x , y ← z x,\ y←z x, y←z | 把z传送到x和y |
- 下标:表示字段中具体的位;
- 对于指令和数据,按从最高位到最低位(即从左到右)的顺序依次进行编号,最高位为第0位,次高位为第1位,依此类推。
- 下标可以是一个数字,也可以是一个范围。
符号 | 意义 |
---|---|
R e g s [ R 4 ] 0 Regs[R4]_{0} Regs[R4]0 | 寄存器R4的符号位 |
R e g s [ R 4 ] 56 − 63 Regs[R4]_{56-63} Regs[R4]56−63 | R4的最低字节 |
-
Mem:表示主存;
- 按字节寻址,可以传输任意个字节。
-
上标:用于表示对字段进行复制的次数。
符号 | 意义 |
---|---|
0 32 0^{32} 032 | 一个32位长的全0字段 |
- 符号##:用于两个字段的拼接,并且可以出现在数据传送的任何一边。
符号 | 意义 |
---|---|
R e g s [ R 8 ] 32 − 63 ← 32 ( M e m [ R e g s [ R 6 ] ] 0 ) 24 # # M e m [ R e g s [ R 6 ] ] Regs[R8]_{32-63} ←_{32} (Mem[Regs[R6]]_{0})^{24} \#\# Mem[Regs[R6]] Regs[R8]32−63←32(Mem[Regs[R6]]0)24##Mem[Regs[R6]] | 以R6的内容作为地址访问内存,得到的字节按符号位扩展为32位后存入R8的低32位,R8的高32位(即Regs [ R 8 ] 0 − 31 [R8]_{0-31} [R8]0−31)不变。 |
2.6.5.3 load、store和ALU指令简介
- load和store指令
指令举例 | 指令名称 | 含 义 |
---|---|---|
LD R2, 20(R3) | 装入双字 | R e g s [ R 2 ] ← 64 M e m [ 20 + R e g s [ R 3 ] ] Regs[R2]←_{64} Mem[20+Regs[R3]] Regs[R2]←64Mem[20+Regs[R3]] |
LW R2, 40(R3) | 装入字 | R e g s [ R 2 ] ← 64 ( M e m [ 40 + R e g s [ R 3 ] ] 0 ) 32 # # M e m [ 40 + R e g s [ R 3 ] ] Regs[R2]←_{64} (Mem[40+Regs[R3]]_{0})^{32}\ \#\# \ Mem[40+Regs[R3]] Regs[R2]←64(Mem[40+Regs[R3]]0)32 ## Mem[40+Regs[R3]] |
LB R2, 30(R3) | 装入字节 | R e g s [ R 2 ] ← 64 ( M e m [ 30 + R e g s [ R 3 ] ] 0 ) 56 # # M e m [ 30 + R e g s [ R 3 ] ] Regs[R2]←_{64} (Mem[30+Regs[R3]]_{0})^{56}\ \#\# \ Mem[30+Regs[R3]] Regs[R2]←64(Mem[30+Regs[R3]]0)56 ## Mem[30+Regs[R3]] |
LBU R2,40(R3) | 装入无符号字节 | R e g s [ R 2 ] ← 64 0 56 # # M e m [ 40 + R e g s [ R 3 ] ] Regs[R2]←_{64} 0^{56}\ \#\#\ Mem[40+Regs[R3]] Regs[R2]←64056 ## Mem[40+Regs[R3]] |
LH R2, 30(R3) | 装入半字 | R e g s [ R 2 ] ← 64 ( M e m [ 30 + R e g s [ R 3 ] ] 0 ) 48 # # M e m [ 30 + R e g s [ R 3 ] ] # # M e m [ 31 + R e g s [ R 3 ] ] Regs[R2]←_{64}\\ (Mem[30+Regs[R3]]_{0})^{48} \ \#\# \ Mem[30+Regs[R3]]\ \#\#\ Mem[31+Regs[R3]] Regs[R2]←64(Mem[30+Regs[R3]]0)48 ## Mem[30+Regs[R3]] ## Mem[31+Regs[R3]] |
L.S F2, 60(R4) | 装入半字 | R e g s [ F 2 ] ← 64 M e m [ 60 + R e g s [ R 4 ] ] # # 0 32 Regs[F2]←_{64} Mem[60+Regs[R4]]\ \#\#\ 0^{32} Regs[F2]←64Mem[60+Regs[R4]] ## 032 |
L.D F2, 40(R3) | 装入双精度浮点数 | R e g s [ F 2 ] ← 64 M e m [ 40 + R e g s [ R 3 ] ] Regs[F2]←_{64} Mem[40+Regs[R3]] Regs[F2]←64Mem[40+Regs[R3]] |
SD R4, 300(R5) | 保存双字 | M e m [ 300 + R e g s [ R 5 ] ] ← 64 R e g s [ R 4 ] Mem[300+Regs[R5]]←_{64} Regs[R4] Mem[300+Regs[R5]]←64Regs[R4] |
SW R4, 300(R5) | 保存字 | M e m [ 300 + R e g s [ R 5 ] ] ← 32 R e g s [ R 4 ] Mem[300+Regs[R5]]←_{32} Regs[R4] Mem[300+Regs[R5]]←32Regs[R4] |
S.S F2, 40(R2) | 保存单精度浮点数 | M e m [ 40 + R e g s [ R 2 ] ] ← 32 R e g s [ F 2 ] 0 − 31 Mem[40+Regs[R2]]←_{32} Regs[F2]_{0-31} Mem[40+Regs[R2]]←32Regs[F2]0−31 |
SH R5, 502(R4) | 保存半字 | M e m [ 502 + R e g s [ R 4 ] ] ← 16 R e g s [ R 5 ] 48 − 63 Mem[502+Regs[R4]]←_{16} Regs[R5]_{ 48-63} Mem[502+Regs[R4]]←16Regs[R5]48−63 |
- ALU指令
- 寄存器-寄存器型(RR型)指令或立即数型
- 算术和逻辑操作:加、减、与、或、异或和移位等
指令举例 | 指令名称 | 含义 |
---|---|---|
DADDU R1, R2, R3 | 无符号加 | Regs[R1]← Regs[R2]+ Regs[R3] |
DADDIU R4, R5, #6 | 加无符号立即数 | Regs[R4]← Regs[R5]+6 |
LUI R1, #4 | 把立即数装入到一个字的高16位 | R e g s [ R 1 ] ← 0 32 # # 4 # # 0 16 Regs[R1]← 0^{32} \ \#\#\ 4 \ \#\#\ 0^{16} Regs[R1]←032 ## 4 ## 016 |
DSLL R1, R2, #5 | 逻辑左移 | R e g s [ R 1 ] ← R e g s [ R 2 ] < < 5 Regs[R1]← Regs[R2]<<5 Regs[R1]←Regs[R2]<<5 |
DSLT R1, R2, R3 | 置小于 | I f ( R e g s [ R 2 ] < R e g s [ R 3 ] ) R e g s [ R 1 ] ← 1 e l s e R e g s [ R 1 ] ← 0 If(Regs[R2]< Regs[R3]) \\ Regs[R1]← 1\\ else\ Regs[R1]←0 If(Regs[R2]<Regs[R3])Regs[R1]←1else Regs[R1]←0 |
- R0的用法
- R0的值永远是0,它可以用来合成一些常用的操作。例如:
- DADDIU R1,R0,#100 给寄存器R1装入常数100
- DADD R1,R0,R2 把寄存器R2中的数据传送到寄存器R1
- R0的值永远是0,它可以用来合成一些常用的操作。例如:
2.6.6 MIPS的控制指令
由一组跳转和一组分支指令来实现控制流的改变
-
跳转指令
- 根据跳转指令确定目标地址的方式不同以及跳转时是否链接,可以把跳转指令分成4种。
- 确定目标地址的方式
- 把指令中的
26
位偏移量左移2
位(因为指令字长都是4个字节)后,替换程序计数器的低28
位; - 间接跳转:由指令中指定的一个寄存器来给出转移目标地址。
- 把指令中的
- 跳转的两种类型
- 简单跳转:把目标地址送入程序计数器。
- 跳转并链接:把目标地址送入程序计数器,把返回地址(即顺序下一条指令的地址)放入寄存器
R31
。
-
分支指令(条件转移)
- 分支条件由指令确定
- 例如:测试某个寄存器的值是否为零
- 提供一组比较指令,用于比较两个寄存器的值。
- 例如:“置小于”指令
- 有的分支指令可以直接判断寄存器内容是否为负,或者比较两个寄存器是否相等。
- 分支的目标地址
- 由16位带符号偏移量左移两位后和PC相加的结果来决定
- 一条浮点条件分支指令:通过测试浮点状态寄存器来决定是否进行分支。
- 分支条件由指令确定
典型的MIPS控制指令
指令举例 | 指令名称 | 含义 |
---|---|---|
J name | 跳转 | P C 36 ⋅ ⋅ 63 ← n a m e < < 2 PC_{36··63}← name<<2 PC36⋅⋅63←name<<2 |
JAL name | 跳转并链接 |
R
e
g
s
[
R
31
]
←
P
C
+
4
;
P
C
36
⋅
⋅
63
←
n
a
m
e
<
<
2
;
Regs[R31]←PC+4;PC_{36··63}←name<<2;
Regs[R31]←PC+4;PC36⋅⋅63←name<<2; ( ( P C + 4 ) - 2 27 ) ≤ n a m e < ( ( P C + 4 ) + 2 27 ) ((PC+4)-2^{27})≤name<((PC+4)+2^{27}) ((PC+4)-227)≤name<((PC+4)+227) |
JALR R3 | 寄存器跳转并链接 | R e g s [ R 31 ] ← P C + 4 ; P C ← R e g s [ R 3 ] Regs[R31]←PC+4;PC← Regs[R3] Regs[R31]←PC+4;PC←Regs[R3] |
JR R5 | 寄存器跳转 | P C ← R e g s [ R 5 ] PC← Regs[R5] PC←Regs[R5] |
BEQZ R4, name | 等于零时分支 |
i
f
(
R
e
g
s
[
R
4
]
=
=
0
)
P
C
←
n
a
m
e
;
if(Regs[R4]== 0)\ PC←name ;
if(Regs[R4]==0) PC←name; ( ( P C + 4 ) - 2 17 ) ≤ n a m e < ( ( P C + 4 ) + 2 17 ) ((PC+4)- 2^{17})≤name<((PC+4)+2^{17}) ((PC+4)-217)≤name<((PC+4)+217) |
BNE R3, R4, name | 不相等时分支 |
i
f
(
R
e
g
s
[
R
3
]
!
=
R
e
g
s
[
R
4
]
)
P
C
←
n
a
m
e
if(Regs[R3]!= Regs[R4])\ PC←name
if(Regs[R3]!=Regs[R4]) PC←name ( ( P C + 4 ) - 2 17 ) ≤ n a m e < ( ( P C + 4 ) + 2 17 ) ((PC+4)-2^{17})≤name<((PC+4)+2^{17}) ((PC+4)-217)≤name<((PC+4)+217) |
MOVZ R1, R2, R3 | 等于零时移动 | i f ( R e g s [ R 3 ] = = 0 ) R e g s [ R 1 ] ← R e g s [ R 2 ] if(Regs[R3]==0)\ Regs[R1]← Regs[R2] if(Regs[R3]==0) Regs[R1]←Regs[R2] |
2.6.7 MIPS的浮点操作
-
由操作码指出操作数是单精度(SP)或双精度(DP)
- 后缀S:表示操作数是单精度浮点数
- 后缀D:表示是双精度浮点数
-
浮点操作
- 包括加、减、乘、除,分别有单精度和双精度指令。
-
浮点数比较指令
-
根据比较结果设置浮点状态寄存器中的某一位,以便于后面的分支指令BC1T(若真则分支)或BC1F(若假则分支)测试该位,以决定是否进行分支。
总结:
- 指令系统分为三大类;
- 寻址方式;
- 赫夫曼编码的指令优化;
- RISC和CISC的发展和改进;
- 操作数的类型和大小;
- MIPS指令系统结构。