指令集基本原理

计算机体系结构背景

简单介绍一下,计算机体系结构狭义上是指关于指令集的设计(广义上的概念会在下一篇中定义),从我们学习的各种编程语言出发,向上是各种应用的编写,向下是程序语言如何编译成计算机所能执行的二进制文件,这里就需要和指令集打交道。高级语言一般不涉及到内存和硬件,在编译过程中指令集的体系结构会起到相当大的影响,比如调用指令集的ADD方法,在不同的指令集中有不同的实现,包括是否使用寄存器,使用多少寄存器,这会影响到程序执行速度以及程序的大小,体系结构也是主要是指令集的设计。

  • 桌面计算机强调涉及整数和浮点数类型的程序性能,很少考虑程序规模。

  • 服务器主要用于数据库、文件服务器和WEB应用,浮点性能的重要性远低于整数和字符串。

  • 个人移动设备和嵌入式应用看中成本和消耗。

指令集体系结构分类

处理器的内部存储类型包括:栈、累加器、寄存器组。指令集中如何使用这些存储操作数分为:

  • 栈体系结构:操作数隐式位于栈顶部

  • 累加器体系结构:操作数为隐式累加器,即累加器的结果为自己的输入

  • 通用寄存器体系结构只有显示操作数:要么是存储器(寄存器-存储器体结构),要么是寄存器(寄存器-寄存器体系结构,也可以称为载入-存储寄存器体系结构)。其中寄存器-存储器体系结构可以用任意指令访问存储器,寄存器-寄存器体系结构只能用载入和存储访问存储器。存储器-存储器体系结构将所有操作数都存在存储器中,今天已经不存在了。

1980之后几乎所有的新体系结构都使用载入-存储寄存器体系结构。原因有三个:1.寄存器快于存储器。2.对于编译器来说,使用寄存器的效率要高于其他形式的存储器。3.寄存器可以保存变量,可以降低内存访问量、提交代码密度(寄存器的名称尾数少于内存地址的位数)

根据以上分类,可以找到指令集的两个特性:

  • 一个ALU(algorithm logic unit,算数逻辑单元)指令有两个还是三个操作数。在三操作数中,指令包含一个目标操作数和两个源操作数,在二操作数中,其中一个操作数即是源操作数也是目标操作数。
  • ALU指令中有多少可以是内存地址。

载入-存储体系结构是(0,3),即0个内存地址和三操作数的指令集。典型的有:ARM、MIPS、RISC-V。优势:简单、固定长度指令编码,指令执行所需要的时钟数相似,因为都是操作寄存器。劣势:指令数多余指令中有存储器的体系结构。指令多,指令密度低增大了程序规模。

寄存器-存储器体系结构是(1,2),即1个内存地址和二操作数的指令集。典型的有:Intel 80 86。优势:无需单独的载入指令就可以访问数据,可以有很好的指令密度。劣势:由于在二元计算中源操作数会被销毁,所以操作数不是等价的,每条指令的时钟数会随操作数的位置变化。

目前基本上都是RISC类型的指令集体系结构,虽然Intel支持8086体系结构,但是其在内部使用硬件将8086体系结构转换为类RISC指令。

扩展阅读:

RISC-V手册·:http://staff.ustc.edu.cn/~llxx/cod/reference_books/RISC-V-Reader-Chinese-v2p12017.pdf

存储器寻址方式

解释存储器地址

一个体系结构必须定义如何解释存储器地址以及如何指定这些地址。

解释存储器地址,如何对一个较大堆行中的字节进行排序:

大端模式:高位字节放在内存的低地址端,低位字节放在内存的高地址端。直观上的模式。

小端模式:高位地址放在内存的高地址端,低位字节放在内存的低地址端

对齐访问,在许多计算机中,对大于1字节的对象的访问必须是对齐的。如果A mod s=0,则在字节地址A对大小为s字节对象的访问是对齐的。

寻址方式

体系结构中如何指定所要访问的对象的地址。

体系结构至少需要支持:位移量寻址、立即数寻址和寄存器间接寻址,因为据统计这三种寻址方式使用最多。

参考:https://www.cnblogs.com/Hardworking666/p/17374792.html#1_10

操作数的类型和大小

常见的操作数类型包括:字符(8位)、半字(16位)、字(32位)、单精度浮点(1字)和双精度浮点(2字)。几乎所有的计算机都遵循了相同的浮点标准——IEEE标准754。某些特定的处理器除外。

对于商业应用程序,一些体系结构支持一种二进制格式,称之为压缩十进制或二进制编码十进制——用4个位对0至9的数值进行编码,两个十进制数位被压缩到两字节中。

参考:https://en.wikipedia.org/wiki/Binary-coded_decimal

指令集中的操作

指令集中操作包括:算数与逻辑、数据传送、控制、系统、浮点、十进制、字符串以及图形类型。执行最多的是指令集中的简单操作。

控制流指令

控制流种类:

  • 条件分支
  • 跳转
  • 过程调用
  • 过程返回
控制流指令的寻址方式

PC相对指令:提供一个将被加到程序计数器(PC)的位移量

动态寻址:如果在编译时不知道目标位置,必须有一种动态指定目标的方法。只需要给出包含目标地址的寄存器名称即可。

寄存器间接跳转的用途(目标地址在编译时是未知的):

  • case或switch语句
  • 虚拟函数或虚拟方法,存在于面向对象语言中
  • 高阶函数或函数指针,允许以参数的形式传递函数
  • 动态共享库,允许仅当程序实际调用一个库时才在运行时加载和链接库
过程调用

过程调用和返回包括控制转移,还可能设计状态保存过程,至少必须将返回地址保存在某个地方。

在保存寄存器时,有两种方式:

  • 由调用者保存:发出调用的过程必须宝座它希望在调用返回后访问的寄存器
  • 被调用者保存:被调用的过程必须保存它想使用的寄存器

由于不同过程可能是分开编译的,在编译过程中编译器想知道被调用者保存的集群器是什么时候分配的会比较困难,因此大多数编译器会采用由调用者返回。

指令集编码

变长编码:允许对所有操作使用所有寻址方式

定长编码:将操作和寻址方式合并到操作吗中

在变长编码和定长编码之间做选择是,权衡的是程序规模和处理器译码的难易程度。更看重代码规模的架构师会选择变长编码,更看重性能的架构师会选择定长编码。