7. MMX 和 SIMD 指令技术
第 3 版 · OCR 辅助 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照
## 7. MMX 和 SIMD 指令技术
多媒体数据处理中存在大量具有共同特征的操作,可以提供一种同时对多个数据元素进行相同操作的指令,这种指令称为SIMD(Single Instruction Multi Data)指令。Intel公司于1997年首次提供了一组称为MMX技术的指令,这组指令可以对8个64位寄存器中的数据进行处理,每个64位数据可以是8字节,或4字,或两个双字,或一个64位的4字。
## 8. 程序的机器级表示
不管用哪种语言编写的程序最终都必须被转换成用0和1表示的机器代码,也就是指令序列。因为机器代码可读性差,所以引入了与机器语言——对应的符号化表示语言,称为汇编语言。机器语言和汇编语言都是机器级表示语言。程序的机器级表示主要是编译程序和解释程序的任务。但是,对于指令集体系结构(ISA)的设计者来说,也必须了解高级语言与机器级表示语言之间的对应关系。
通常,高级语言中的赋值语句被转换成由若干运算类指令构成的指令序列;像if语句这样的选择结构程序段被转换为一段包含分支指令(条件转移指令)的指令序列;像for语句这样的循环结构程序段被转换为包含无条件跳转指令和分支指令的被循环执行的指令序列。此外,对于高级语言源程序中的过程调用,也必须在ISA中有相应的支持过程调用的机制,例如,必须提供过程调用(转子)指令和过程返回指令,同时,因为需要支持嵌套调用和递归调用,所以还必须提供对栈和栈帧的操作指令等。
## 9. 指令系统举例
以MIPS指令集体系结构为例说明。MIPS是典型的RISC风格指令系统架构,采用了三地址指令格式,提供了32个32位通用寄存器,寄存器编号占5位。
通用寄存器的汇编表示可以使用名称(如$ t0),也可以用编号(如$ 0)。此外,MIPS中还提供了32个32位浮点寄存器,两个32位浮点寄存器构成一个64位双精度浮点寄存器。为了实现乘除运算,MIPS还提供了两个乘商寄存器Hi和Lo,这两个寄存器不需要在指令中明显给出,因而,与程序计数器PC一样,它们没有寄存器编号。
<!-- page: 81 -->
# 4.3 基本术语解释
## 指令(instruction)
指令是计算机硬件能够识别并直接执行的操作命令。用二进制序列表示,由操作码和操作数或操作数的地址码等字段组成。
## 指令系统(instruction set)
指令系统也称为指令集,是计算机中所有指令的集合。
## 指令集体系结构(instruction set architecture, ISA)
指令集体系结构是计算机硬件与系统软件之间的接口,其核心部分是指令集,同时还包含数据类型和数据格式定义、寄存器设计、I/O空间的编址和数据传输方式、中断结构、计算机状态的定义和切换、存储保护等。
## 指令字长(instruction length)
指令字长是一条指令的二进制代码位数。有定长指令字格式和变长指令字格式两种不同的指令系统。
## 定长指令(fixed length instruction)
定长指令是指机器中所有指令的位数是相同的,目前定长指令字大多是32位指令字。
## 变长指令(variable length instruction)
变长指令是指机器中的指令有长有短,但每条指令的长度一般都是8的倍数。
## 操作码(operate code)
操作码是指令中用于指出操作性质的字段。一般分为定长操作码和扩展操作码。定长操作码是指机器中所有指令的操作码字段位数相同。扩展操作码是指机器中指令的操作码字段位数不是都相同,也称为不定长操作码。
## 地址码(address code)
地址码是指令中用于指出操作数地址的字段。一条指令中一般有多个地址码字段。地址码字段的个数与许多因素有关。一个地址码字段可能是一个立即数;可能是操作数所在的存储单元地址;可能是一个间接的存储单元地址的地址;可能是寄存器编号;可能是I/O端口号;可能是一个形式地址等。
## 大端序(big endian ordering)
大端序是指操作数在内存存放时,指令中给出的地址是操作数最高有效字节(MSB)所在的地址。例如,假设一个32位数据“12345678H”的地址为100,则 12H、34H、56H 和 78H 分别存放在第100、101、102 和 103 号单元中。例如,IBM S/370,Motorola 680x0 等是大端序机器。
## 小端序(little endian ordering)
小端序是指操作数在内存存放时,指令中给出的地址是操作数最低有效字节(LSB)所在的地址。例如,假设一个32位数据“12345678H”的地址为100,则 12H、34H、56H 和 78H 分别存放在第103、102、102 和 100 号单元中。例如,Intel 80x86 等是小端序机器。
## 字地址(word address)
每个内存单元都有一个地址,假定机器中一个字为32位,按字节编址,那么字地址就是
<!-- page: 82 -->
指具有 4 的倍数的那些地址,如 0、4、8、12……;对应的还有半字地址(2 的倍数,如 0、2、4、6……)、双字地址(8 的倍数,如 0、8、16……)等。
## 边界对齐(boundary alignment)
有些机器在操作数存放到内存单元时,要求按照相应的地址边界进行对齐。例如,假定机器中一个字为 32 位,按字节编址,那么一个 32 位的数据(如一个 float 型的变量或 32 位的 int 型整数变量等)就必须存放在字地址上;一个 16 位的数(如 16 位 short 型的短整数等)就必须放在半字地址上;而 8 位的数据(如 char 型字符)就可以放在任何边界地址上而不需要对齐。
## 累加器(accumulator)
在中央处理器 CPU 中,累加器(accumulator,ACC)是一种暂存器,用来存储计算所产生的中间结果。早期机器中没有通用寄存器组,这种情况下,如果没有像累加器这样的暂存器,那么在每次计算后就必须要把结果写回到内存,然后可能还要再读回来。这样,就会增加访问内存的次数,降低程序运行的效率。利用累加器进行中间结果存储的一个典型例子就是把一组数字加起来。一开始累加器设定为零,每个数字依序被加到累加器中,当所有数字都被加入后,结果才写回内存中。
## 程序计数器(Program Counter, PC)
程序计数器又称指令计数器或指令指针 IP,是一个特殊的地址寄存器,专门用来存放下一条要执行指令的地址。因为本身它是个寄存器,所以也称为指令指针寄存器或指令地址寄存器。通常程序是顺序执行的,程序的指令序列在内存中一般也是按连续地址存放的。在开始运行程序之前,总是将第一条指令的地址放入 PC;每取出一条指令并执行后,控制器就使 PC 的内容自动增量(加上当前指令的长度),指明下一条要执行的指令所存放的存储单元地址,从而可以控制指令的顺序执行;在遇到需要改变程序执行顺序的情况时,一般由转移类(或称跳转类)指令将转移目标地址存入程序计数器,即可实现程序的跳转。
## 指令寄存器(Instruction Register, IR)
指令寄存器用来保存当前正在执行的一条指令。当执行一条指令时,先从存储器取出指令,然后送至指令寄存器。指令寄存器中的操作码部分被送到指令译码器(Instruction Decoder, ID),经 ID 译码识别这条指令的功能后,送出具体的操作控制信号。
## 程序状态字(Program Status Word, PSW)
程序状态字表示程序运行状态的一个二进制位序列。PSW 一般包含一些反映指令执行结果的标志信息(如进位标志、溢出标志、符号标志等)和设定的一些状态信息(如中断允许/禁止状态、管理程序/用户程序状态等)。
## 程序状态字寄存器(Program Status Word Register, PSWR)
程序状态字寄存器指用来存放程序状态字的寄存器。
## 标志寄存器(flags register)
标志寄存器是指 80x86 体系结构中用来表示程序状态和标志的寄存器。
## 栈(stack)
栈是一块特殊的存储区,采用“先进后出”的方式进行访问,主要用来在程序切换(如过程调用)时保存各种信息。栈底固定不动,栈顶浮动,用一个专门的寄存器(SP)来作为栈顶指针。从栈生长的方向来分,有“自顶向下”和“自底向上”两种。从栈的位置来分,有硬栈和
<!-- page: 83 -->
软栈。硬栈的栈区由寄存器实现,软栈的栈区由内存实现。
## 栈指针(Stack Pointer,SP)
栈指针是一个特殊的地址寄存器,用来存放栈的栈顶指针。如果是硬栈,则栈顶指针是栈顶寄存器的编号;如果是软栈,则栈顶指针是栈顶所指向的存储单元的地址。
## 双目运算(two-operand operate)
双目运算是需要两个操作数才能进行的运算。例如,加、减、乘、除、与、或等算术及逻辑运算都是双目运算。
## 单目运算(one-operand operate)
单目运算是只需要一个操作数就能进行的运算。例如,取负、取反等算术及逻辑运算都是单目运算。
## 寻址方式(addressing mode)
在程序执行过程中,需要取指令和操作数,确定指令和操作数的存放位置的方式称为寻址方式。确定指令存放位置的过程称为指令寻址,确定操作数存放位置的过程称为数据寻址。
## 有效地址(effective address)
有效地址是存储器操作数所在存储单元的地址。若不采用虚拟存储机制,则有效地址是主存地址;若采用虚拟存储机制,则有效地址是虚拟地址。
## 立即寻址(immediate addressing)
指令中的地址码直接给出操作数本身称为立即寻址。
## 直接寻址(direct addressing)
指令中的地址码给出的是操作数所在的存储单元地址,称为直接地址。
## 间接寻址(indirect addressing)
指令中的地址码给出的是操作数所在的存储单元地址所在的存储单元地址,称为间接地址。
## 寄存器寻址(register addressing)
指令中的地址码给出的是操作数所在的寄存器的编号,称为寄存器寻址。
## 寄存器间接寻址(register indirect addressing)
指令中的地址码给出的是操作数所在的存储单元的地址所存放的寄存器的编号,称为寄存器间接寻址。
## 偏移寻址(displacement addressing)
偏移寻址方式下,指令通过某种方式给出一个形式地址和一个基地址(往往在某个寄存器中),经过相应的计算(基地址加形式地址)得到操作数所在的存储单元地址。具体的偏移寻址方式有变址寻址、相对寻址和基址寻址。
## 变址寻址(indexing addressing)
变址寻址方式下,指令中的地址码给出一个形式地址,并且隐含或明显地指定一个寄存器作为变址寄存器,变址寄存器的内容(变址值)和形式地址相加,得到操作数的有效地址,根据有效地址到存储器中访问,去取操作数或写运算结果。
## 变址寄存器(index register)
变址寄存器是一个特殊的地址寄存器,用来存放变址寻址方式下的变址值,通常是数组
<!-- page: 84 -->
元素的下标值等。
## 相对寻址(relative addressing)
相对寻址方式下,指令中的形式地址给出一个位移量 $D$,而基准地址由程序计数器(PC)提供。即有效地址 $\mathrm{EA} = (\mathrm{PC}) + D$。位移量可正可负,也就是说,要找的可以在当前指令前 $D$ 个单元处的信息,也可以是当前指令后 $D$ 个单元处的信息。
## 基址寻址(base addressing)
基址寻址方式下,指令中的地址码给出一个形式地址作为位移量,并且隐含或明显地指定一个寄存器作为基址寄存器,基址寄存器的内容和形式地址相加,得到操作数的有效地址,根据有效地址到存储器中访问,去取操作数或写运算结果。
## 基址寄存器(base register)
基址寄存器是一个特殊的地址寄存器,用来存放基址寻址方式下的基准地址。通常是一个用户程序的首地址,或一块存储区的首地址。
## 栈寻址(stack addressing)
栈寻址方式下,操作数被指定在栈中。栈寻址总是从栈顶取操作数,运算后的结果自动放到栈顶。栈顶的位置由一个专门的栈指针 SP 来指示。所以,指令中无须给出操作数地址,是一种零地址指令。与栈有关的操作有人栈(PUSH)、出栈(POP)和运算类操作。
## 通用寄存器(General Purpose Register, GPR)
一般把用户可访问寄存器称为通用寄存器。这些寄存器都有一个编号,在指令中用编号标识寄存器。所以执行指令时,指令中的寄存器编号要送到一个地址译码器进行译码,然后才能选中某个寄存器进行读写。通用寄存器可以用来存放操作数或运算结果,或作为地址指针寄存器、变址寄存器、基址寄存器等。有的处理器架构把 PC 也作为一个通用寄存器使用。
- R-R 型指令(Register-Register type instruction)
- R-R 型指令是两个操作数都在寄存器中的指令。
- R-S 型指令(Register-Storage type instruction)
- R-S 型指令是一个操作数在寄存器中,另一个操作数在主存单元中的指令。
- S-S 型指令(Storage-Storage type instruction)
- S-S 型指令是两个操作数都在主存单元中的指令。
- 数据传送指令(data transfer instruction)
- 数据传送指令是指将数据在寄存器和寄存器之间、存储单元和寄存器之间进行传送的指令。
- 取数指令(Load)
- 取数指令特指将数据从存储单元取到寄存器的指令,也称为装入指令。
- 存数指令(Store)
- 存数指令特指将数据从寄存器保存到存储单元的指令,也称为存储指令。
- 相对转移(relative jump)
- 相对转移的转移目标地址通过 PC 的值加上一个偏移量形成。所以,转移到的目的地和当前指令的位置有关。
<!-- page: 85 -->
76
绝对转移 (absolute jump)
绝对转移的转移目标地址由指令指定的一个绝对地址确定,而与当前指令的位置没有关系。
条件转移 (conditional jump, branch)
条件转移是一种分支指令,也称为条件分支。根据前面指令或本条指令执行的结果确定是跳转到转移目标地址处执行,还是顺序执行。
无条件转移 (unconditional jump)
无条件转移是一种直接跳转指令,执行完本条指令后,无条件地跳转到目标转移地址处执行。
过程 (procedure)
构造过程或子程序是程序员进行模块化程序设计的一种手段,通常程序员把一个大的任务分解成一些子任务,每个子任务用一个过程来实现,这样做,一方面使得程序容易理解,另一方面也使过程可以被多个程序使用,即可重用代码。
过程调用 (procedure call)
一个过程调用包括将数据(以过程参数和返回值的形式出现)和控制从一个过程传递到另一个过程。此外,在进入被调用过程后,必须为被调用过程的局部变量分配空间,并在退出过程时释放这些空间。
跳转链接指令 (Jump and Link instruction)
跳转链接指令是用于将控制从调用过程转移到被调用过程的指令。在 MIPS 指令系统中称为跳转链接指令 jal。在有些机器的指令系统中也被称为过程调用指令(Call instruction)或转子指令(意思为转移到子程序),如在 x86 体系结构中的 call 指令。这类调用指令中需要给出被调用过程的首地址。其操作过程主要包括保存返回地址到特定的寄存器并跳转到被调用过程。
返回地址 (return address)
返回地址是跳转链接指令(调用指令)后面一条指令的地址,即被调用过程执行完后必须返回的返回点。
返回指令 (Return instruction)
返回指令是用于从被调用过程控制转回到调用过程的指令。该指令从某个特定的寄存器或栈顶取得返回地址,并按返回地址进行跳转。
调用过程 (caller)
调用过程是指在过程调用中,通过过程调用指令调用一个过程或子程序的过程。有时也称为主程序。
被调用过程 (callee)
被调用过程是指用过程调用指令(或称为跳转链接指令)所调用的过程或子程序。被调用过程最后必须用返回指令返回到调用过程中调用指令后面的那条指令继续执行。
叶过程 (leaf procedure)
叶过程是不调用其他过程的过程。
过程帧 (procedure frame)
大多数机器只提供简单的过程调用指令和返回指令。一个过程调用中的参数传递、被
<!-- page: 86 -->
调用过程中的局部变量的分配和释放等还需要另外的机制来实现。主要是通过栈 attack) 来实现,栈用来传递过程参数、存储返回信息、保存寄存器的内容和过程局部变量等。为单个过程分配的那部分栈称为过程帧,也称为栈帧 (stack frame)。
## 帧指针 (frame pointer, fp)
在程序运行过程中,可能会有多个过程嵌套调用,每个未返回的过程都有一个过程帧,当前正在执行的过程的过程帧称为当前帧。它有两个指针定界,一个是指示当前帧底部的帧指针 fp,另一个是指示当前帧顶部的栈指针 sp。
## 源程序文件 (source program file)
源程序文件是用某种高级语言书写的源程序文件,如 C 语言源程序文件 *.c 等。
## 汇编语言程序文件 (assemble language source file)
汇编语言程序文件是用某种汇编语言书写的源程序文件,如 UNIX 系统中的汇编语言源程序文件 *.s 或 MS-Windows 中的 *.asm 文件等。
## 可重定位目标文件 (relocatable object file)
可重定位目标文件是经编译程序和汇编程序对高级语言源程序进行翻译处理所得到的机器语言目标程序文件,如 UNIX 系统中的 *.o 文件或 MS-Windows 系统中的 *.obj 文件等。其中,包含目标文件头、文本段(机器代码)、数据段、重定位信息、符号表、调试信息等。
## 可执行目标文件 (executable object file)
可执行目标文件是由若干可重定位目标文件经链接生成的能被直接装入存储器执行的文件。它通常与可重定位目标文件有相同的格式。可执行目标文件中的模块可以调用其他动态链接库中的函数。
## 链接程序 (linker)
链接程序是把编译或汇编好的多个可重定位目标程序文件和一些库函数目标文件链接生成一个可执行目标文件的工具软件。
## 装入程序 (loader)
装入程序也称为加载器或加载程序,是用于将可执行目标文件读入存储器并启动执行的软件模块。一般来说,可执行文件的装入是由操作系统内核来实现的,因此,加载程序属于操作系统的一部分。
## 全局指针 (global pointer) 寄存器
操作系统通过执行装载程序把一个可执行文件装入内存时,必须按照一定的规律使用内存。基于 MIPS 处理器的系统,通常将用户空间分为三部分:第一部分是从 0x00400000 开始的正文段,通常也称为只读代码段,对应于程序代码和只读数据存储区;第二部分是数据段,分成静态数据段和动态数据段两部分,静态数据段从 0x10000000 开始,其上是动态数据段,也称为堆区,该区域随着程序的执行被动态分配,向上扩展(从低地址向高地址增长);第三部分是栈区,处于用户空间的最上端,从地址 0x7FFFFFFF 开始向下扩展(从高地址向低地址增长)。MIPS 处理器为了方便地实现对静态数据区的访问,把地址 0x10008000 放在一个专门的寄存器 $gp 中,将其作为指向静态数据段的全局指针。这样在 Load 和 Store 指令中用 16 位偏移量就可以对静态数据段的前 64KB 的区域进行方便的存取。
<!-- page: 87 -->
## 伪指令(pseudo instruction)
伪指令是指在汇编语言程序中使用的但并不存在的“假”指令。假定在汇编语言程序中需要经常使用某个基本功能,但是该功能并不能用单条机器指令来实现,而是需要用多条指令来实现,此时,一般用一条简洁自然的伪指令来表示多条机器指令,它们在功能上等价。汇编程序在对汇编语言源程序进行汇编时,将伪指令转换为等价的机器指令序列。
## CISC (Complex Instruction Set Computer)
CISC 即复杂指令集计算机。早期的计算机为了增加功能和更好地支持高级语言而不断地增加新的指令类型,使 CPU 可以直接实现复杂的指令操作。这种指令系统中的指令,功能复杂,寻址方式多,指令长度可变,指令格式多样。因而,采用这种指令系统的计算机被称为复杂指令集计算机。
## RISC (Reduced Instruction Set Computer)
RISC 即精简指令集计算机。这种计算机采用简化的指令系统,指令集中只包含程序中常用的指令,只有 Load 和 Store 指令才能访存,运算类指令只能是 R-R 型,提供大量通用寄存器以减少访存次数,采用流水线方式执行指令,控制器用硬连阵列逻辑实现,并采用优化的编译技术。