processor_archtecture


处理器架构

​ 一个处理器支持的指令和指令的字节级编码称为它的指令集体系结构(Instruction-Set Architecture, ISA)。不同的处理器“家族”,例如 Intel IA32 和 x86-64、IBM/Freescale Power 和 ARM处理器家族,都有不同的ISA。一个程序编译成在一种机器上运行,就不能在另一种机器上运行。ISA在编译器编写者和处理器设计人员之间提供了一个概念抽象层,编译器编写者只需要知道允许哪些指令,以及他们是如何编码的;而处理器设计者必须建造出执行这些指令的处理器。

​ 本章将通过CSAPP中的Y86-64进行介绍。

一、Y86-64指令集体系结构

1. 程序员可见的状态

​ 如图1-1,Y86-64程序中的每条指令都会读取或修改处理器状态的某些部分,这称为程序员可见状态,这里的“程序员”既可以是人也可以是产生机器级代码的编译器。

​ Y86-64的状态和x86类似:

  • 15个程序寄存器
  • 条件码:ZF(零),SF(符号),OF(溢出)
  • PC:程序计数器
  • Stat:程序执行的总体状态
  • DMEM:内存

1-1 程序员可见状态

2. Y86-64指令

​ 图1-2给出了Y86-64 ISA中各个指令的简单描述。Y86-64基本上是x86-64指令集的一个子集,它只包括8字节整数操作,寻址方式较少,操作也较少。因为只有8字节数据,所以下述称为字(word)不会有任何歧义。图中左边是指令的汇编码表示,右边是字节编码。图 1-3给出了其中一些指令更详细的内容。汇编代码格式类似于x86-64的ATT格式。

图 1-2

3. 指令编码

​ 每条指令需要1~10个字节不等,这取决于需要哪些字段。每条指令的第一个字节表明指令的类型。这个字节分为两个部分,每部分4位:高四位是代码(code)部分,低四位是功能(function)部分。如图 1-2 所示,代码值分为0~0xB。功能值只有在一组相关指令共用一个代码时才有用。图 1-3给出了部分指令的具体编码。

图 1-3 Y86-64指令集的功能码

​ 如图 1-4所示,15个寄存器都有一个相对应的寄存器标识符(register ID)。Y86-64与x86-64的编号相同。程序寄存器存在CPU中的寄存器文件中,这个寄存器文件就是一个小的、以寄存器ID作为地址的随机访问存储器。当需要指明不访问任何寄存器时,就用ID值0xF来表示。

图 1-4

​ 有些指令需要一个附加的4字节常数字。如图 1-2,常数字使用IA32一样的小端法编码。例如,用十六进制来表示指令rmmovq %rsp, 0x123456789abcd(%rdx)的字节编码。通过图1-2,我能能知道rmmovq的第一个字节为40。源寄存器%rsp应该编码放在rA字段中,而基址寄存器%rdx应该编码放在rB字段中,根据图1-4中的寄存器编号,得到寄存器指示符字节42。最后,偏移量编码放在8字节的常数字中。首先0x123456789abcd需要在前面补充上0变成8字节,字节序为:00 01 23 45 67 89 ab cd,小端法为:cd ab 89 67 45 23 01 00。将它们连接起来就得到指令的编码:4042cdab896745230100

​ 指令集的一个重要性质就是字节编码必须有唯一的解释。任何一个字节序列要么是唯一的指令序列的编码,要么就不是一个合法的字节序列。这个性质保证了处理器可以无二义性地执行目标代码程序。反过来说,如果不知道一段代码序列的起始位置,就不能准确的确定怎样将序列划分为单独的指令。对于试图直接从目标代码字节序列中抽取出机器级程序的反汇编程序和其他一些工具来说,这就带来了问题。

4. Y86-64异常

​ 对于Y86-64来说,程序员可见的状态(图1-1)包括状态码Stat,它描述程序执行的总体状态。这个代码可能的值如下图所示。在设计中,任何AOK以外的代码都会使处理器停止。

​ 对于Y86-64,遇到这些异常的时候,我们就简单地让处理器停止执行指令。在更完整的设计中,处理器通常会调用一个异常处理程序(exception handler),这个过程被指定用来处理遇到的某种类型的异常。如cpp的std::terminate

1-5 Y86-64状态码

5. Y86-64程序

​ 图1-6给出了一个C函数的x86-64Y86-64汇编代码,Sum函数计算一个整数数组的和,Y86-64代码与x86-64代码遵循了相同的通用模式:

图1-6 汇编程序比较

x86-64代码是由GCC编译器产生的。Y86-64代码与之类似,但有以下不同点:

  • Y86-64将常数加载到寄存器(第2~3行),因为它在算术指令中不能使用立即数。
  • 要实现从内存读取一个数值并将其与一个寄存器相加,Y86-64代码需要两条指令(第8~9行),而x86-64只需要一条addq指令(第5行)。
  • 我们手工编写的Y86-64实现有一个优势,即subq指令(第11行)同时还设置了条件码,因此GCC生成的代码中的testq指令(第9行)就不是必需的。不过为此,Y86-64代码必须用andq指令(第五行)在进入循环之前设置条件码。

文章作者: cfrost
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 cfrost !
  目录