早期的大多数计算机都是采用堆栈结构或累加器结构的指令集,但是自1980年以后,大多数计算机都陆续采用了通用寄存器结构,优势体现在:
1. 跟其他的CPU内部存储单元一样,寄存器的访问速度比存储器快。
2. 对编译器而言,能更加容易、有效地分配和使用寄存器。
3. 寄存器可以用来存放变量。
寻址方式
立即数寻址和偏移寻址是使用频率最高的两种寻址方式。
上图的横坐标表示立即数的位数。我们可以看出,最常用的是较小的立即数。在指令集结构设计中,至少要将立即数的大小设置为8~16位。
指令集结构的功能设计实际上就是确定软硬件的功能分配,主要考虑的因素有3个:速度、成本、灵活性。
用硬件实现的特点是速度快、成本高、灵活性差,用软件来实现的特点是速度慢、价格便宜、灵活性好,一般来说,对出现频率高的基本功能应首选硬件来实现。
对指令集的基本要求是:完整性、规整性、高效率和兼容性。
有两种不同的设计策略,CISC即复杂指令集计算机,它是增强指令功能,把越来越多的功能交由硬件来实现,并且指令的数量也是越来越多,如x86。RISC即精简指令集计算机,它是尽可能把指令集简化,不仅指令集条数少,而且指令的功能也比较简单,如MIPS。
CISC指令集结构存在以下问题:
1. 各种指令使用频度相差悬殊。据统计,只有20%的指令使用频度较高,占运行时间的80%,而其余80%的指令只在20%的运行时间内才会用到。
2. 指令集庞大,指令条数多,许多指令的功能又很复杂。这使得硬件非常复杂。
RISC指令集结构一般应当遵循以下原则:
1. 指令条数少而简单。
2. 采用简单而有统一的指令格式,病减少寻址方式。指令字长都为32位或64位。
3. 指令的执行在单个时钟周期内完成。
4. 只有load和store指令才能访问存储器,其他指定操作都是在寄存器之间进行。
5. 大多数指令都采用硬连逻辑来实现。
6. 强调优化编译器的作用,为高级语言程序生成优化的代码。
7. 充分利用流水线技术来提高性能。
控制指令
控制指令是用来改变控制的,分为四种:分支、跳转、过程调用和过程返回,其中分支指令使用的最为频繁。
本书约定:当指令是无条件改变控制流时,称为跳转指令;而当控制指令是有条件改变控制流是,称为分支指令。
实现分支指令的三种方法为:条件码、条件寄存器、比较与分支。
在控制指令中,必须给出转移的目标地址。指定转移地址最常用的方法是在指令中提供一个偏移量,由该偏移量和程序计数器(PC)的值相加而得出目标地址。这种寻址方式称为PC相对寻址。
操作数的类型和大小
计算机系统所能处理的数据类型很多,在设计和实现时,需要研究哪些用硬件实现,哪些用软件实现,并研究它们的实现方法。
数据表示是指计算机硬件能直接识别、指令集可以直接调用的数据类型,一般用硬件实现,如定点数、逻辑数、浮点数、字符、字符串等。
数据结构则不同,它是指由软件进行处理和实现的各种数据类型。数据结构是研究这些数据类型的逻辑结构与物理结构之间的关系,并给出相应的算法。
表示操作数类型的方法有两种:(1)有指令中的操作码指定操作数的类型,这是最常用的方法。(2)给数据加上标识,有数据本身给出类型。
指令格式的设计
x86采用变长编码格式,这种格式可以减少目标代码的长度。
MIPS采用定长编码格式,这种格式可以提高编译性能。
MIPS指令集结构
MIPS64有32个64位通用寄存器:R0,R1,...,R31。R0的值永远是0。此外还有32个64位的浮点数寄存器:F0,F1,...,F31。
MIPS的数据寻址方式只有立即数寻址和偏移量寻址两种。寄存器间接寻址是通过把0作为偏移量来实现的,16位绝对寻址是通过把R0(值永位0)作为寄存器来完成的。所有的存储器访问都必须是边界对齐的。
所有的指令都是32位的,可分为四大类:load和strore、ALU操作、分支与跳转、浮点数操作。
简单跳转很简单,就是把目标地址送入程序计数器。而跳转并链接则要比简单跳转多一个操作:把返回地址放入寄存器31。
浮点指令对浮点寄存器中的数据进行操作,并有操作码指出操作数是单精度还是双精度。在指令助记符中,用后缀S表示单精度浮点数,用D表示双精度浮点数。