15

vesperW · 1月25日

软硬协同优化 (4):指令集设计概述

1. 基本概念

指令集架构(ISA)作为软件与硬件之间的接口,它定义了软件如何控制处理器,指定了处理器能够做什么以及如何完成,是汇编语言程序员、编译器编写者和应用程序程序员可见的机器部分。下图直观地展示了 ISA 在计算机系统中所处的层次,所有的程序最终都会以指令的形式供处理器来执行。

image.png

指令集架构的重要性主要体现在如下几个方面:

  • 了解指令集可以做什么
  • 编译器如何利用这些指令可以帮助开发人员编写更高效的代码
  • 帮助开发人员理解编译器的输出,利好程序调试
  • 可以自定义指令,针对性加快一些负载的执行

2. 指令集设计原则

指令集提供了用户与硬件交互的唯一方式,其设计的变化会同时影响软硬件,所以指令集的设计会遵循一定的原则。其基本原则主要有:

  • 兼容性。这是指令集的关键特性。在较长时间内保持指令集不变并保持向前兼容,对于生态的完整性有着重要的作用。例如x86指令集,虽然背负了很多历史包袱,需要支持早期过时的指令,但其兼容性与Intel在市场上获得的巨大成功是密不可分的。
  • 通用性。市场上的应用需求是多样化的,如网络应用、科学计算、视频解码、商业应用等,为了适应各种应用需求,通用的指令集的功能必须完备。但是针对特定应用,可以设计专有指令,这时则不需要强调通用性。
  • 高效性。指令集架构还要便于硬件的设计和优化。对同一指令集,既可以通过采用先进、复杂的技术增加硬件复杂性来提升性能,也可以用成熟、简单的技术简化硬件来满足需求。
  • 安全性。安全性在如今的计算机系统中越发地重要,指令集的设计应当考虑为各种安全特性提供支持,如提供保护模式等。

3. 指令集分类

根据操作数存储形式的不同,可以将指令集类型分成堆栈型累加器型寄存器型,寄存器型又可以进一步分为:

  • 寄存器-存储器型,即可以用任意指令来访问存储器。
  • 载入-存储器型,即只能用载入和存储指令来访问存储器。
  • 存储器-存储器型,即所有的操作数都保存在存储器中。

下表展示了各寄存器型指令集的优劣势,当然这些优劣点并不是绝对的,是一种定性的描述,实际的影响还取决于编译器和实现策略。

image.png

另一种比较常用的分类方式是依据指令长度的不同,指令系统可分为复杂指令系统(Complex Instruction Set Computer,简称CISC)、精简指令系统(Reduced Instruction Set Computer,简称RISC)和超长指令字(Very Long Instruction Word,简称VLIW)指令集三种。CISC中的指令长度可变;RISC中的指令长度比较固定;VLIW本质上来讲是多条同时执行的指令的组合,由编译器发现并行度,无须硬件进行判断。

4. 指令集设计

指令集由若干条指令及其操作对象组成。每条指令都是对一个或者多个操作的描述,主要包括操作码操作数。操作码规定指令功能,例如加减法;操作数指示操作对象,包含数据类型、访存地址、寻址方式等内容的定义。

不论是何种类型的指令集,都必须定义如何解释存储器地址以及如何指定这些地址。

如何解释存储器地址主要是明确根据地址和长度会访问到什么对象,几乎所有的指令集都是字节寻址,提供了对字节、半字、字和双字的访问。在存储器中,对于一个较大对象中的字节排序有两种约定方式:一种为小端模式,即低位低地址;另一种为大端模式,即高位低地址。在采用不同排序方式的计算机之间交换数据时,字节顺序会成为一个问题。此外许多处理器对于寻址对齐与否是敏感的,主要是非对齐寻址会增加硬件复杂度,同时也影响处理器性能。

指令集是如何指定存储地址的呢?采用的是寻址方式。寻址方式通常是通过指定常量和寄存器的形式来指定实际的存储器地址。常见的寻址方式如下表所示:

image.png

上表中,Reg 表示寄存器,Mem[Reg[r1]] 表示存储器内容,其地址由 Reg[r1] 给出。

如何指定操作数的类型呢?现下最常用的方法是通过在操作码中进行编码来指定操作数的类型。而操作数的类型又可以有效确定其大小,常见的操作数类型包括字符(8位)、半字(16位)、字(32位)、双字(64位)、单精度(1个字)和双精度(2个字)。

那么如何设计指令操作呢?在体系架构中有一条经验规律:执行最多的指令是一个指令集中的简单操作。这些简单操作在所执行指令中占据很高的比例,加速这些指令也就能加速整体的处理器性能。通用计算中的指令操作可分为:

  • 算术与逻辑:整型算术与逻辑运算
  • 数据传输:载入存储操作
  • 控制:分支、跳转、函数调用与返回等
  • 系统:系统调用
  • 浮点:浮点运算

针对一些专有领域,会增加操作类型来进行加速,比如字符串操作、图形相关的操作等。

指令最终会以二进制的形式供处理器执行,这就需要对指令集进行编码。而指令集各环节的设计不仅影响程序编译后的大小,还会影响处理器的实现。处理器必须对编码的指令进行译码,快速找到操作和操作数。在对指令集进行编码时,架构师主要平衡以下几点设计趋向:

  • 允许尽可能多的寄存器和寻址方式
  • 考虑寄存器字段和寻址方式字段的大小对指令平均大小的影响
  • 希望编码后的指令长度易于微架构的高效实现,如流水线处理等

常见的指令集编码有三种:

  • 变长编码,几乎允许对所有操作使用所有寻址方式,这种编码方式在存在许多寻址方式和操作时是最佳选择。
  • 定长编码,通常所有指令的大小相同,当寻址方式和操作数较少时,效果最好。
  • 混合编码,提供多种指令长度,以缩小代码大小。

指令集设计时所作的决策决定了架构师是否能够在变长指令编码和定长指令编码之间选择。通常看重代码规模多于性能的架构师会倾向变长编码,而看重性能多于代码规模的架构师则会倾向定长编码。

5. 不同类型的指令集对比

下表列出了 CISC 和 RISC 之间特点差异:

image.png

VLIW指令集

由于在一条指令中封装了多个并行操作,其指令的长度比 RISC 或 CISC 的指令要长,因此起名为超长指令集(VLIW),其主要的设计思想:

  • 将多个相互无依赖的指令封装到一条超长的指令字中
  • 处理器中有对应数量的算术逻辑单元完成相应的指令操作
  • 指令之间的依赖性和调度由编译器来完成

VLIW 架构中,指令级并行的发现与指令执行顺序的调度完全交由编译器完成,优势是极大的简化了硬件设计,主要的具体优化点有:

  • 硬件不需要动态调度,易做到高频率
  • 不需要在 VLIW 指令中进行依赖项检查,简单的指令多发射硬件
  • 取指后分发到不同能单元,不需要进行指令对齐/分配

但其主要的劣势也是过于依赖编译器发掘并行度:

  • 编译器需要找到N个独立的操作
  • 如果无法找到,需要在一个 VLIW 指令中插入空操作
  • 导致并行性降低,同时代码大小增加
  • 当执行宽度(N)、指令延迟、功能单元改变时需要重新编译进行匹配
  • 同步执行会导致独立操作停止。在延迟时间最长的指令完成之前,任何指令都不能执行

VLIW 实现的功能与超标量处理器相似之处 —— 一次发射并完成多个操作,但他们之间一个重要的区别 —— VLIW硬件不负责挖掘指令间的并行度,因此与超标量处理器相比,极大地降低了硬件的设计复杂度。当前VLIW主要是在嵌入式市场(如:DSP),获得了成功应用。

6. 写在最后

在20世纪70年代,内存非常昂贵,为了最小化存储需求,复杂指令集(CISC)应运而生,其特点是密集的指令编码、可变长度的指令和少量的寄存器。这种设计简化了软件和编译器的设计,但也显著提高了硬件的复杂性。到20世纪80年代,随着芯片技术的发展,存储资源问题缓解,精简指令集(RISC)针对复杂指令集的缺点,在设计上简化了指令功能、指令编码、访存类型。而计算发展至今,不同指令集之间的原始设计差异影响逐渐变小,需要将工艺制程、微架构及ISA三个属性结合起来后才能判断孰优孰劣。但是从收获知识的角度来说,了解指令集的原始差异依旧有重要的意义。

7. 参考

  1. Burger D, Keckler S W, McKinley K S, et al. Scaling to the End of Silicon with EDGE Architectures[J]. Computer, 2004, 37(7): 44-55.
  2. 计算机体系结构基础
  3. [美]John L. Hennessy, David A. Patterson著. 计算机体系结构:量化研究方法[M]. 贾洪峰译. 北京:人民邮电出版社,2022
  4. https://www.extremetech.com/extreme/323245-risc-vs-cisc-why-its-the-wrong-lens-to-compare-modern-x86-arm-cpus
作者:徐银
来源:毕昇编译

推荐阅读

欢迎大家点赞留言,更多Arm技术文章动态请关注极术社区嵌入式客栈专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。
推荐阅读
关注数
2836
内容数
166
分享一些在嵌入式应用开发方面的浅见,广交朋友
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息