第二章 计算机指令集结构设计计算机的指令集结构也称为指令系统,它是硬件机器所支持的全部指令集合,它是机器语言程员所看到的机器的最主要的属性之一。
指令集结构设计的最基本问题是软硬件划分。
指令集结构设计包括确定指令格式、类型、操作以及操作数的访问方式等
2.1 指令集结构的分类
2.1.1 指令集结构分类根据五个因素对计算机指令集结构进行分类:
(1) 在 CPU中操作数的存储方法
(2) 指令中显式表示的操作数个数
(3) 操作数的寻址方式
(4) 指令集所提供的操作类型
(5) 操作数的 类型和大小
CPU中操作数的存储方法,是各种指令集结构之间最主要的区别所在。
1,CPU中用来存储操作数的存储单元主要有:
2,指令中的操作数可以显式给出,也可以隐式地给出。
3,CPU对操作数的不同存取方式
堆栈
累加器
寄存器 /存储器
CPU对操作数的不同存取方式
CPU提供的暂存器每条 ALU指令显式表示的操作数个数运算结果的目的地访问显式操作数的过程堆栈 0 堆栈 Push/Pop
累加器 1 累加器 Load/Store累加器寄存器 /存储器
2/3 寄存器或存储器
Load/Store 寄存器或存储器
4,根据 CPU内部存储单元 类型进行分类,可以分为:
堆栈型指令集结构
累加器型指令集结构
通用寄存器型指令集结构例 C=A+B表达式在这三种类型指令集结构上的实现方法。假设 A,B,C均是保存在存储器单元中,
且 A和 B的值在运算过程中一直被保持。
C=A+B表达式在这三种类型指令集结构上的实现方法堆栈 累加器 寄存器
(寄存器-存储器 )
寄存器
(寄存器-寄存器)
PUSH A LOAD A LOAD R1,A LOAD R1,A
PUSH B ADD B ADD R1,B LOAD R2,B
ADD Store C Store C,R1 Add R3,R1,R2
POP C Store C,R3
三种类型指令集结构的优缺点指令集结构类型优点 缺点堆栈型 是一种表示计算的简单模型;指令短小 。
堆栈不能被随机访问,从而很难生成有效代码 。 同时,由于堆栈是瓶颈,
所以很难被高效地实现 。
累加器型 减小了机器的内部状态;指令短小 。
由于累加器是唯一的暂存器,这种机器的存储器通信开销最大 。
寄存器型 是代码生成最一般的模型 。
所有操作数均需命名,且显式表示,
因而指令比较长 。
2.1.2 通用寄存器型指令集结构的分类早期的计算机中采用堆栈指令集结构和累加器指令集结构比较多,但现代 CPU,通用寄存器型指令集结构已成为指令集结构的主流,原因:
1,通用寄存器型指令集结构的 主要优点
(1) 使编译器有效地使用寄存器 ;
(2) 在表达式求值方面,比其它类型指令集结构具有更大的灵活性 ;
(3) 寄存器可以用来存放变量。
◆ 减少存储器的通信量,加快程序的执行速度。
(因为寄存器比存储器快)
◆ 可以用更少的地址位来寻址寄存器,从而可以有效改进程序的目标代码大小。
3,两种主要的指令特性 能够将通用寄存器指令集结构
( GPR)进一步细分
(1) ALU指令到底有两个或是三个操作数?
◆ 有三个操作数的指令:两个源操作数一个结果操作数
2,CPU需要设置多少个寄存器呢?
主要由编译器使用寄存器的情况来决定
为表达式求值保留一些寄存器
为传递参数保留一些寄存器
用剩下的寄存器来保存变量
◆ 有两个操作数的指令:一个操作数既作为源操作数,也作为目的操作数。
(2) 在 ALU指令中,有多少个操作数可以用存储器来寻址,也即有多少个存储器操作数?
一般来说,ALU指令有 0~ 3个存储器操作数 。
ALU指令中,存储器操作数个数和操作数个数的所有可能组合,以及相应的机器实例
ALU指令中存储器操作数个数
ALU指令中操作数的最大个数机器实例
0 2 IBM RT-PC
3 SPARC,MIPS
1 2 PDP-10,IBM 360,
Motorola 68000
3 IBM360的部分指令
2 2 PDP- 11,部分 IBM360
指令
3
3 3 VAX
(3) 通用寄存器指令集结构进一步细分为三种类型:
(4) 常见的三种通用寄存器型指令集结构的优缺点注,表中 (m.n)的含义是,指令的 n个 操作数中有 m个 存储器操作数。
寄存器 --- 寄存器型 ( R-R:register-register)
寄存器 --- 存储器型 ( R-M:register-memory)
存储器 --- 存储器型 ( M-M:memory-memory)
指令集结构类型优 点 缺 点寄存器-寄存器型(
0,3)
简单,指令字长固定,是一种简单的代码生成模型,各种指令的执行时钟周期数相近 。
和指令中含有对存储器操作数访问的结构相比,指令条数多,因而其目标代码较大 。
寄存器-存储器型 (
1,2)
可以直接对存储器操作数进行访问,容易对指令进行编码,
且其目标代码较小 。
指令中的操作数类型不同 。 在一条指令中同时对一个寄存器操作数和存储器操作数进行编码,将限制指令所能够表示的寄存器个数 。 由于指令的操作数可以存储在不同类型的存储器单元,所以每条指令的执行时钟周期数也不尽相同 。
存储器-存储器型
( 3,3)
是一种最紧密的编码方式,无需,浪费,
寄存器保存变量 。
指令字长多种多样 。 每条指令的执行时钟周期数也大不一样,对存储器的频繁访问将导致存储器访问瓶颈问题 。
2.2 寻址技术
1,寻址方式是指令对操作数的访问方式
在一个计算机系统中,机器语言的寻址方式通常有多种
当前通用寄存器结构的指令集结构中所使用的一些寻址方式寻址方式 指令实例 含 义寄存器寻址 Add R4,R3 Regs[R4]←Regs[R 4]+ Regs[R3]
立即值寻址 Add R4,#3 Regs[R4]←Regs[R 4]+ 3
偏移寻址 Add R4,100(R1) Regs[R4]←Regs[R 4]+ Mem[100+Regs[R1]]
寄存器间接寻址 Add R4,(R1) Regs[R4]←Regs[R 4]+ Mem[Regs[R1]]
索引寻址 Add R3,(R1 +
R2)
Regs[R3]←Regs[R 3]+ Mem[Regs[R1]+Regs[R2]]
直接寻址或绝对寻址
Add R1,(1001) Regs[R1]←Regs[R 1]+ Mem[1001]
存储器间接寻址 Add R1,@(R3) Regs[R1]←Regs[R 1]+ Mem[Mem[Regs[R3]]]
自增寻址 Add R1,(R2)+ Regs[R1]←Regs[R 1]+ Mem[Regs[R2]]
Regs[R2]←Regs[R 2]+ d
自减寻址 Add R1,-(R2) Regs[R2]←Regs[R 2]- d
Regs[R1]←Regs[R 1]+Mem[Regs[R2]]
缩放寻址 Add
R1,100(R2)[R3]
Regs[R1]←Regs[R 1] + Mem[100 + Regs[R2] +
Regs[R3]*d]
寄存器寻址,变量值在寄存器中 。
立即值寻址,常量访问偏移寻址,访问局部变量寄存器间接寻址,用指针访问变量 ( 指针值在寄存器中 )
索引寻址,数组访问直接寻址或绝对寻址,静态变量访问存储器间接寻址,用指针访问变量 ( 指针值在存储器中 )
自增寻址,遍历数组,有时用于实现栈结构自减寻址,遍历数组,有时用于实现栈结构缩放寻址,数组访问
1,在通用寄存器指令集结构中,一般是利用 寻址方式 指明指令中的操作数是一个常数、一个寄存器操作数,抑或是一个存储器操作数。
3,寻址方式使用情况统计结果
( VAX指令集结构的机器,gcc,Spice和 Tex 基准程序)
1% 0%
24%
43%
32%
6%
16%
3%
17%
55%
1%
6%
11%
39%
40%
0%
10%
20%
30%
40%
50%
60%
70%
′?
′¢
÷?
ó
°?
2

2?
°?
2

′?
÷?
ó
°?
2
á¢

°?
2

ò?
°?
2
Tex Spice gcc
立即值寻址方式 和 偏移寻址方式 的使用频率十分高。
4,各种偏移量字段大小的使用情况寄存器-寄存器型指令集结构的机器上( MIPS)
运行 SPECint92基准程序集
( compress,espresso,eqntott,gcc,li)
运行 SPECfp92基准程序集
( dudoc,ear,hydro2d,mdljdp2,su2cor)
x轴的标记是对偏移量大小进行 log2( ·)运算所得,
也就是偏移量字段的位数。
0%
5%
10%
15%
20%
25%
30%
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
SPE Cin t 9 22aá? í3μSPECfp922 a á? í3μ
ò°? 22?
ê? μ? °? 2? ±è
◆ 程序所使用的偏移量大小分布十分广泛;
◆ 较小的偏移量和较大的偏移量均占有相当大的比例;
◆ 将偏移量字段的大小设置为 12~ 16位 。这种长度可以支持上述 75%~ 99% 基于偏移寻址方式的数据访问中偏移量大小的表示。
5,在一种 Load/Store型指令集结构上,一些指令使用立即值寻址方式的频率。
比较指令 和 ALU指令 使用立即值寻址方式十分频繁。
10%
87%
58%
35%
45%
77% 78%
10%
0%
20%
40%
60%
80%
100%
Load? · á? ±è· á? ALU? · áù óD?· á?
Díù ·? μù
6,不同立即值大小的使用分布情况将立即值的大小设置为 8~ 16位,可以覆盖所有使用 立即值寻址 方式指令的 50%~ 80% 。
0%
10%
20%
30%
40%
50%
60%
0 4 8 12 16 20 24 28 32
±í ê? á¢?′?μ μ êù
°?
2?
±è
gcc Tex spice
2.3 指令集结构的功能设计
1,指令集中操作的 分类操作类型 实 例算术和逻辑运算整数的算术和逻辑操作:加,减,与,或等 。
数据传输 Load/Store
控制 分支,跳转,过程调用和返回,自陷等 。
系统 操作系统调用,虚拟存储器管理等 。
浮点 浮点操作:加,乘等 。
十进制 十进制加,十进制乘,十进制到字符的转换 。
字符串 字符串移动,字符串比较,字符串搜索等 。
图形 象素操作,压缩 /解压操作等 。
2,一种指令集结构中的指令到底要支持哪些类型的操作?
这就是指令集结构功能设计的基本问题。
两种截然不同的方向:
◆ 复杂指令集计算机( CISC)
强化指令功能,实现软件功能向硬件功能转移。
◆ 精简指令集计算机( RISC)
尽可能地降低指令集结构的复杂性,以达到简化实现,提高性能的目的。
当今指令集结构功能设计的一个主要趋势。
2.3.1 CISC计算机指令集结构的功能设计
CISC的含义是复杂指令集计算机 (Complex
Instruction Set Computer)
起源于 1964年的 IBM360系列机以后有 DEC公司的 PDP-11,VAX11/730,VAX11/750、
VAX11/780
到九十年代 IBM390系列机微机方面有 Intel 80x86,Motorola 68020等
CISC的主要特点:
1)指令系统复杂,表现在:
指令数多,一般大于 100条寻址方式多,一般大于 4种指令格式多,一般大于 4种
2)绝大多数指令需要多个机器时钟周期方可完成
3)各种指令都可访问存储器
4)采用微程序控制
5)有专用寄存器
6)难以进行优化编译
CISC结构追求的目标:
强化指令功能,减少程序的指令条数,以达到提高性能的目的。
增强指令功能主要是从如下几个方面着手:
一、面向目标程序增强指令功能
提高运算型指令功能
提高传送指令功能
增加程序控制指令功能二、面向高级语言和编译程序改进指令系统
1.增加对高级语言和编译系统支持的指令功能
◆ 对源程序中各种高级语言语句进行使用频度的统计与分析,对于使用频度高的语句,
可以设置专门的指令或采取措施增加相应指令的功能,以提高其编译速度和执行速度。
◆ 从面向编译程序,尤其是从优化代码生成的角度进行考虑,增加指令集结构的规整性来改进指令系统。
规整性,没有或尽可能减少例外的情况和特殊的应用,以及所有运算都能对称、均匀地在存储器单元或寄存器单元之间进行。
2.高级语言计算机指令系统
(1) 面向高级语言 (HL)的机器缩小机器语言和高级语言的 语义差距。
(2) 间接执行型高级语言机器高级语言和机器语言是 一一对应 的,用汇编的方法 (可以用软件实现,也可以用硬件实现 )
把高级语言源程序翻译成机器语言程序。
(3) 直接执行型高级语言机器高级语言就作为机器语言,直接由硬件或固件对高级语言源程序的语句逐条进行解释以执行它。
三、面向操作系统的优化实现改进指令系统操作系统的实现在很大程度上取决于体系结构的支持。
1,主要表现在对以下方面的支持
中断处理
进程管理
存储管理和保护
系统工作状态的建立与切换
2,设置指令
支持系统工作状态和访问方式转移的指令
支持进程转移的指令
支持进程同步和互斥的指令
CISC结构存在的缺点:
(1) 在 CISC结构的指令系统中,各种指令的使用频率相差悬殊。
(2) CISC结构指令系统的复杂性带来了计算机体系结构的复杂性,这不仅增加了研制时间和成本,而且还容易造成设计错误。
(3) CISC结构指令系统的复杂性给 VLSI设计增加了很大负担,不利于单片集成。
(4) CISC结构的指令系统中,许多复杂指令需要很复杂的操作,因而运行速度慢。
(5) 在 CISC结构的指令系统中,由于各条指令的功能不均衡性,不利于采用先进的计算机体系结构技术(如流水技术)来提高系统的性能。
例如,80x86处理器支持几百条指令,但常用的指令只有十条,使用频率占 96%
执行频率排序 80X86指令 指令执行频率(%执行指令总数)
1 Load 22%
2 条件分支 20%
3 比较 16%
4 Store 12%
5 加 8%
6 与 6%
7 减 5%
8 寄存器-寄存器间数据移动
4%
9 调用 1%
10 返回 1%
合 计 96%
Intel 80X86最最常用的十条指令
2.3.2 RISC计算机指令集功能的设计
RISC的含义是精简指令集计算机 (Reduced Instruction
Set Computer)
起源于 1975年的 IBM 801小型计算机
1979年由加州大学伯克莱分校 Patterson教授等提出 RISC
这一术语,并研制出 RISC-I,RISC-II计算机随后,斯坦福大学于 1981年,1986年分别推出了 MIPS
RISC计算机和 MIPS2000 RISC计算机
1987年 SUN公司基于伯克莱分校 RISC计算机提出的 SPARC
系列工作站,使得工作站成为高速小型 /微型计算机的代表,RISC技术在工作站中被普遍采用 。
1988年 Motorola推出的 MC 88000 RISC计算机,RISC技术以背普遍接受 。
目的使得计算机体系结构更加简单、更加合理和更加有效,克服 CISC结构的缺点,使机器速度更快,程序运行时间缩短,从而提高计算机系统的性能。
RISC的主要特点:
一,精简指令系统
1)指令条数少,一般小于 100条
2)基本寻址方式少,一般 2?3种
3)指令格式少,一般 2?3种
4)指令长度一致 (32位 )
二,以寄存器 -寄存器方式工作,除了 Load/Store
指令访问存储器外,其余指令只访问寄存器三,除了 Load/Store指令访问存储器外,所有指令在一个机器时钟周期完成,并采用流水线技术四,使用较多的通用寄存器,一般至少 32个,不允许有专用寄存器五,大多采用硬联线控制,少用或不用微程序实现六,采用针对体系结构特点的优化编译技术,防止或减少流水线中出现的相关性,保证流水线畅通 。
设计原则
选取使用频率最高的指令,并补充一些最有用的指令;
每条指令的功能应尽可能简单,并在一个机器周期内完成;
所有指令长度均相同;
只有 Load和 Store操作指令才访问存储器,
其它指令操作均在寄存器之间进行;
以简单有效的方式支持高级语言。
2.3.3 控制指令控制指令:是指能够改变计算机控制流,也即被处理的指令序列的执行顺序的指令 。
控制流的各种改变情况,
1)转移指令
2)过程调用和返回
3)协同程序
4)中断和自陷转移指令还分两种,
1)无条件转移指令,跳转 (Jump)
2)有条件转移指令:分支或条件分支 (Branch或
Conditional Branch)
1,控制指令的使用频率
(一台 Load/Store型指令集结构的机器:
SPECint92,Specfp92基准程序)
13%
6%
81%
11%
4%
87%
0%
20%
40%
60%
80%
100%
μ÷ ó? / 2μ?× ì÷ 3a ìú 2§
Díù ·? μù
改变控制流的大部分指令是 条件分支指令 。
表示分支条件的技术测试分支条件的方法 优 点 缺 点条件码
( CC)
在程序的控制下,由 ALU操作设置特殊的位 。
可以自由设置分支条件 。
CC是额外状态,条件码限制了指令顺序,因为必须从一条指令将分支条件信息传送到分支指令 。
条件寄存器根据比较结果测试条件寄存器 。 简单 占用了一个寄存器 。
比较且分支比较操作是分支指令的一部分,
通常这种比较是受一定限制的 。
一条指令完成了两条指令的功能 。
分支指令的操作增多 。
2,常用的三种表示分支条件的技术及其优缺点
3,分支目标地址的表示
PC-相对寻址,在指令中提供一个和程序计数器( PC)的值相加的偏移量。
(1) 有效地缩短指令中表示目标地址的字段的长度;
(2) 使得代码在执行时与它被载入的位置无关。
关键问题是:
转移目标离当前控制指令的偏移量有多大?
4,过程调用和返回的状态保存两种方法来保存寄存器的内容:
(1),调用者保存,方法在一个调用者调用别的过程时,必须保存调用者所要保存的寄存器,以备调用结束返回后,能够再次访问调用者。
(2),被调用者保存,方法被调用的过程必须保存它要用的寄存器,保证不会破坏过程调用者的程序执行环境,并在过程调用结束返回时,恢复这些寄存器的内容。
2.4 操作数的类型、表示和大小操作数类型,机器语言所支持的数据类型操作数类型是软硬件主要界面之一常用的操作数类型有:整数,浮点数,十进制,字符,字符串,向量,堆栈等 。
操作数表示,操作数表示是指可以由硬件直接识别、指令系统可以直接处理的数据类型。
操作数表示也是软硬件主要界面之一 。
目前常用的表示:
(1) 字符,用 ASCII码表示,为一个字节大小;
(2) 整数,用二进制补码表示,其大小可以是 8位、
16位或 32位。
(3) 浮点操作数,单精度浮点( 32位大小)和双精度浮点( 64位大小),
(4) 字符串,将字符串中的每个字符当作一个字节来看待。
5) 十进制操作数
◆ 压缩十进制用 4位二进制数编码数字 0~ 9,然后将两个十进制数字压缩在一个字节中存储。
◆ 二进制编码十进制将十进制数字直接用字符串来表示操作数类型的 两种表示方法
(1) 操作数的类型由操作码的编码指定。
这是最常见的一种方法。
(2) 数据可以附上由硬件解释的标记,由这些标记指定操作数的类型,从而选择适当的运算。
操作数大小
字节
半字( 16位)
单字( 32位)
双字( 64位。
访问不同操作数大小的频率测试统计 SPECint92基准程序和 SPECfp92基准程序对字节、半字、单字和双字四种大小的操作数访问情况。
74%
19%
7%
69%
31%
0%
0%
0%
0% 50% 100%
3?
°? 3?
Díù ·? μù
基准程序对 单字 和 双字 的数据访问具有较高的频率,所以如果选择操作数字段的长度为 32位,那么它可以有效支持
8,16,32位整型操作数,以及 32位浮点操作数的表示。
当然,如果定义操作数字段长度为 64位,则更具有一般性。
2.5 指令集格式的设计
◆ 指令由 操作码 和 地址码 组成。
◆ 指令集格式的设计确定操作码字段和地址码字段的大小及其组合形式,以及各种寻址方式的编码方法。
◆ 设计原则
1,尽可能地增加寄存器数目和寻址方式类型;
2,充分考虑寄存器字段和寻址方式字段对指令平均字长的影响,以及它们对目标代码大小的影响;
3,设计出的指令集格式能够在具体实现中容易 处理。
2.5.1 寻址方式的表示方法
1,两种表示寻址方式的方法
(1) 将寻址方式编码于操作码中,由操作码在描述指令操作的同时,也描述了相应操作的寻址方式;
(2) 为每个操作数设置一个 地址描述符,
由该地址描述符表示相应操作数的寻址方式。
操作码 地址描述符 1 地址码字段 1? 地址描述符 n 地址码字段 n
利用地址描述符表示寻址方式的方法
2,选择哪种表示寻址方式的方法?
由两个因数决定:
(1) 指令集结构所采用的寻址方式种类及其适用范围
(2) 操作码与寻址方式之间的独立程度
2.5.2 指令集格式的选择三种指令集编码格式,
变长编码格式
固定长度编码格式
混合型编码格式操作码 地址描述符 1 地址码 1? 地址描述符 n 地址码 n
1,变长编码格式
◆ 有效减少指令集结构的平均指令长度,降低目标代码的长度。
◆ 使得各条指令的字长和执行时间大不一样。
多数 CISC计算机的指令集结构均是采用这种编码格式。
2,固定长度编码格式将操作类型和寻址方式组合编码在操作码中,所有指令的长度是固定唯一的。
操作码 地址码 1 地址码 2 地址码 3
操作码 地址描述符 地址码操作码 地址描述符 1 地址描述符 2 地址码操作码 地址描述符 地址码 1 地址码 2
混合型编码格式
3,混合型编码格式通过提供一定类型的指令字长,期望能够兼顾降低目标代码长度和降低译码复杂度两个目标。
浮点数表示每个浮点数均由三部分组成:符号位 S、指数部分 E和尾数部分 M
1)单精度格式 (32位 ),S=1位,E=8位,M=23位
2)双精度格式 (64位 ),S=1位,E=11位,M=52位以单精度格式来说明
S,取值 0或 1,表示浮点数值的正或负
M,表示尾数值为 1.M
E,当取值 1?254时,经移码表示指数 -126? +127
( 减 127)
当取值 0时,若 M=0,表示 0
若 M?0,表示非规格化数,尾数值为 0.M
当取值 255时,若 M=0,表示?
若 M?0,表示非数值例如:
1 01111111 1000……
S=1,E=127,M=0.5
值 =(-1)11?2127-127?1.5=-1.5
0 10000001 0100……
S=0,E=129,M=0.25
值 =(-1)01?2129-127?1.25=5
单精度 双精度符号位 1 1
指数位 8 11
尾数位 23 52
总位数 32 64
指数移码 -127 -1023
指数取值范围 -126
+1 27 -1022
+ 10 23
最小规格化数
2
-12 6
2
-1 022
最大规格化数
2
+12 8
2
+1 023
十进制范围
10
- 38
10
+ 38
10
- 308
10
+ 308
2.6 编译技术与计算机体系结构设计本节从编译技术的观点讨论指令集结构设计的 关键目标,
◆ 哪些指令集结构特性可以为生成高质量的程序目标代码提供保证?
◆ 如何才能非常容易地为指令集结构编写出相应的高效编译器?
2.6.1 现代编译器的结构和相关技术现代编译器结构依赖关系 功 能依赖语言 语言预处理 将语言转换到与机器独立 一般中间形式中间表示某些依赖语言 高级优化 比如:过程展开大部分与机器独立 和循环变换较少依赖语言 整体优化 包括全局和开始依赖机器 局部的优化
(如寄存器计数 / 类型) 寄存器分配高度依赖机器 代码生成 仔细选择指令不依赖语言 机器相关的优化编译器设计者的 目标
(1) 保证编译器的正确性,使得所有有效的程序必须能够被正确地编译
(2) 编译出的代码在目标机器上的执行速度要尽可能 快
(3) 编译的速度
(4) 对程序调试的支持
(5) 各种语言之间互操作的可能性由于高级语言和低级语言之间存在很大的语义差别,在完成这样一个复杂转换过程中要兼顾这些内容是很复杂的一件事 。 为了确保正确性,现代编译器一般都采用多遍扫描的方法:每一遍扫描都将一种比较高级的表示形式转换成一种比较低级的表示形式,最终达到机器语言形式 。
同样,优化过程通常也是采用这样一种多遍扫描方法,每一遍扫描进行一种类型的优化处理,直至所有优化处理执行完 。
这种多遍扫描方法引出了一个,按序转换问题,,
前一步转换并不知道它是否一定适合后一步转换,它只能基于某种假设,有一定的盲目性 。
例,全局公共子表达式消去法,
设法找出在同一表达式中出现的公共子表达式,并把第一次公共子表达式计算出的值存放在一个临时变量中,然后它利用该临时变量的值消去表达式中的其它相同公共子表达式的计算。
关键,该临时变量一定要分配给某一寄存器。
问题,后续的优化处理不一定能完成这样的寄存器分配寄存器分配寄存器分配算法均是基于,图着色法,发展而来的。
图着色法的基本思想:
根据将要分配给寄存器的各个可能候选变量和它们的使用范围,构造相干图,然后再用该图来分配寄存器。
( a ) 程序段 ( b )相干图
A =
B = A B
B?
C=
A? C D
D=
D?
C?
( c )着色图 ( d )寄存器分配后的代码
R1 =
A B R2 =
R2?
R2=
C D? R1?
R1 =
R1?
R2?
编译器所实现的优化的几种类型
(1) 高级优化通常是对原始程序进行优化,并将优化结果送到后续的优化扫描中。
(2) 局部优化仅仅是一系列在线性的程序段(也称为基本块)中进行的优化。
(3) 全局优化在局部优化的基础上,考虑到各种分支情况,
对循环和分支进行一系列的优化转换。
(4) 寄存器分配
(5) 基于机器的优化目的是充分利用机器结构的一些特点进行代码优化。
一些实际优化方法优化类型和方法 说 明高级优化 处于或接近源代码级别,与机器独立无关过程集成 用过程体代替过程调用语句。
局部优化 线性代码序列内的优化消去公共子表达式 设法找出在同一表达式中出现的公共子表达式,并把第一次公共子表达式计算出的值存放在一个临时变量中,然后它利用该临时变量值消去表达式中的其它相同公共子表达式的计算。
常数传递 将所有被赋值为常数的变量用该常数的值代替。
降低堆栈的高度 对表达树进行重新组织,尽量减少表达式求值时所需要的资源。
全局优化 非线性代码序列的优化消去公共子表达式 和局部优化中的消去公共子表达式优化类似。
拷贝传递 如果变量 A已经赋值为 X,则用 X代替所有地方的 A。
代码移动 如果在循环中的某段代码均是计算相同的值,则将这段代码移到循环的外部。
消去索引变量 简化 /消去在循环中的数组地址计算。
基于机器的优化 依赖于机器的特点降低计算量 比如,可以用加操作和移位操作来代替一个常数的乘操作。
流水线调度 重新组织指令序列以提高流水线性能。
分支偏移的优化 选择能够达到分支目标的最短分支偏移量。
进行的优化 性能提高的百分比只进行过程集成 10%
只进行局部优化 5%
局部优化+寄存器分配 26%
局部优化+全局优化 14%
局部优化+全局优化+寄存器分配 63%
局部优化+全局优化+寄存器分配+过程集成
81%
8,优化对程序执行性能的影响
2.6.2 现代编译技术对计算机体系结构设计的影响要认识编译技术对计算机体系结构的影响必须了解这样两个 问题:
◆ 如何分配和寻址变量?
用多少个寄存器分配变量比较合适。
◆ 优化技术对指令使用频度有何影响?
什么样的指令更需加快执行速度 。
1,高级语言用来分配数据的三个不同区域
(1) 堆栈用来分配局部变量,可以不通过指针访问。
(2) 全局数据区用来分配静态说明的对象,可以不通过指针访问。
(3) 堆主要用来动态分配一些不适合放在堆栈中的对象,
通过指针访问。
◆ 将寄存器分配给堆栈和全局数据区中未曾采用指针访问的对象。
◆ 对于分配给堆的对象,不能将寄存器分配给它们。
例如 考虑如下代码序列,其中,&”表示返回一个变量的地址,,*” 则是取该地址处保存的值:
在上述代码序列中,就不能够将变量 a分配给寄存器,否则就会生成错误的代码 。
p = &a 取变量 a的地址,并将其保存在 p中;
a = … 直接对变量 a进行赋值;
*p = … 利用指针 p对 a进行赋值;
… a … 访问 a。
2,将存储器访问分成五类
(1) 未分配的访问
(2) 对全局变量的访问
(3) Load/Store访问将存储器中的内容读入寄存器,或将寄存器的内容写回存储器。
(4) 必要的堆栈变量访问
(5) 计算型的访问包括所有对堆变量的存储器访问,以及由指针和数组索引实现的存储器访问等。
在 Load/Store机器上运行 gcc和 Tex基准程序:
í? 2.1 1 ·÷à ′ ′? ′¢?÷2ê °? 2? ±è ′÷ê ù ±ˉ?é
0
0.1
0.2
0.3
0.4
0.5
0.6
0.7
0.8
0.9
1
0 2 4 6 8 10 12 14 16 18 20 22 24 26
′÷ê ù
2?
ê
μ?
°?
2?
±è
Dí μ? 2ê ±× òa μ ±? á? 2ê
Loa d/S to r e 2ê è ±? á? μ? 2ê
′ 2 μ? 2ê
3,各种类型的存储器访问次数占所有存储器访问次数的百分比
上述第 2到第 5种类型的存储器访问次数并不随着机器中配置的寄存器个数的变化而变化;
未分配类型的存储器访问次数,却会随着寄存器个数的增多而减少。
计算型的访问占有相当大的比例。
结论,(1) 增加寄存器对系统性能影响较大,特别是当出现大量,未分配的访问,时
(2) 体系结构应有效支持,计算型的访问,
4.指令数目随优化级别变化的情况
(在 MIPS机器的编译器上采用不同的优化级别分别对基准程序集 SPEC92中的 hydro2d和 li两个基准程序进行编译。)
优化级别 0:不进行优化;
优化级别 1:编译器进行局部优化、代码调度和局部寄存器分配;
优化级别 2:编译器进行全局优化、循环转换和全局寄存器分配;
优化级别 3:除了包含优化级别 2的优化工作之外,
还包含过程集成的优化工作。
图2.12 指 令计数随优化级别的变化情况
0 0.2 0.4 0.6 0.8 1
优化级别0
优化级别1
优化级别2
优化级别3
优化级别0
优化级别1
优化级别2
优化级别3
编译器优化级别指令计数分支/调用 FLOPs Load/Store 整型ALU
hydro2d
li
结论:
优化可以明显地降低程序中指令的条数,但是却增加了分支 /调用等控制指令在程序中所占的比例。
即编译器优化技术可以有效减少整型 ALU指令的条数,但是却难以减少分支指令的条数。在计算机体系结构设计中应该注重支持对控制指令的加速。
2.6.3 计算机体系结构对当前编译技术的影响编译器真正的复杂之处在于要决定哪一种代码顺序最为理想,也即当前编译技术的难点在于优化。
基本原则:
程序中经常出现的部分要尽量加速,而对较少出现的部分要力求正确。
指令集结构的设计原则:
1,规整性要求指令集的三个主要元素(操作、数据类型和寻址方式)必须正交。
正交,上述三种元素中的任何两个元素在指令集结构中彼此独立无关。
2,提供基本指令,而非解决方案,
3,简化取舍标准编译器设计者所面临的最棘手的问题:计算出每段代码采用哪一种指令顺序才是最高效的。
4,对于在编译时就已经知道的量,提供能够将其变为常数的指令。
2.7 DLX指令集结构通用寄存器型指令集结构
Load/Store指令集结构支持偏移寻址,立即寻址,寄存器间接寻址偏移寻址的偏移量应该在 12至 16位立即寻址的立即值大小应该在 8至 16位整形操作数 8位,16位,32位浮点操作数 32位,64位指令长度单一 ( 32位 ),格式简单通用寄存器个数大于等于 16
DLX指令集结构的设计思想:
具有一个简单的 Load/Store指令集;
注重指令流水效率;
简化指令的译码;
高效支持编译器。
2.7.1 DLX指令集结构一,DLX中的寄存器
(1) 32个通用寄存器命名,R0,R1,?,R31
长度,32位寄存器 R0的值总是为 0。
(2) 32个浮点寄存器命名,F0,F1,?,F31
长度,32位可以用来保存 32位的单精度浮点数,或者通过相邻两个浮点寄存器奇偶对 FiFi+1( i = 0,2,4,
,30)来保存双精度浮点数,这种组合而成的 64
位双精度浮点寄存器在 DLX中分别被命名为 F0,F2、
,F28,F30。
(3) 一些特殊的寄存器
( 比如用来保存浮点操作结果信息的浮点状态寄存器 )
可以和通用寄存器相互进行数据传送 。
二,DLX的数据类型
DLX提供了多种长度的整型数据和浮点数据 。
(1) 整型数据有 8位,16位和 32位多种长度 。
(2) 浮点数据有 32位单精度浮点数和 64位双精度浮点数 。
浮点数据表示采用的是 IEEE 754标准 。
当 8位和 16位整型数据载入到寄存器中时,用 0
或数据的符号位来填充 32位通用寄存器中的剩余位。
三,DLX的寻址方式和数据传送
(1) 寻址方式
寄存器寻址
立即值寻址
偏移寻址
寄存器间接寻址
(2) 寄存器寻址字段的大小为 5位,用来表示 32个通用寄存器或浮点寄存器 。
(3) 存储器地址采用的是高端字节表示顺序,存储器按字节寻址,其地址宽度为 32位 。
四,DLX的指令格式寻址方式编码在操作码中 。
指令的字长,32位,其中用 6位表示操作码 。
(4) 通过寄存器(通用寄存器和浮点寄存器)和存储器之间的数据传送操作完成对存储器的访问各种类型指令的格式
I 类型指令
6 5 5 16
操作码 rs1 rd 立即值字节、半字、字的载入和储存;
rd? rs1 op 立即值。
R 类型指令
6 5 5 5 11
操作码 rs1 rs2 rd Func
寄存器-寄存器 ALU 操作,rd? rs1 func rs2 ;
函数对数据的操作进行编码:加、减,? ;
对特殊寄存器的读 / 写和移动。
J 类型指令
6 26
操作码 与 PC 相加的偏移量跳转,跳转并链接,从异常( exception )处自陷和返回。
图 2.13 DLX 的指令格式布局五,DLX中的操作
1,四种类型的操作:
Load和 Store操作
ALU操作
分支和跳转操作
浮点操作
2,约定:
(1) 符号,?”,数据传送操作其后附带一个下标 n,也即,?n” 表示传送一个 n位数据。
(2) 符号,##”:两个域的串联操作
(3) 域的下标:表明从该域中选择某一位 。
域中位的标记是从最高位开始标记,并且起始标记为 0。
下标可以是一个单独的数字 。
如 Regs[R4]0:选择寄存器 R4中内容的符号位 。
下标也可以是一个范围 。
如 Regs[R3]24..31:表示选择寄存器 R3中内容的最低一个字节 。
(4) 上标:表示复制一个域 。
如 024可以得到一个 24位全为 0的一个域 。
(5) 变量 Mem:表示存储器中的一个数组,
存储器按照字节寻址。
举例 R8和 R10,32位寄存器
Regs[R10]16..31?16(Mem[Regs[R8]]0)8 ##
Mem[Regs[R8]]的含义。
DLX中的四种操作类型
1,Load和 Store操作指令实例 指令名称 含 义
LW R1,30 (R2) 载入整型字 Regs[R1] ← 32 Mem[30+Regs[R2]]
LW R1,1000 (R0) 载入整型字 Regs[R1] ← 32 Mem[1000+0]
LB R1,40 (R3) 载入字节 Regs[R1] ← 32 (Mem[40+Regs[R3]]0)24 ## Mem[40+Regs[R3]]
LBU R1,40 (R3) 载入无符号字节 Regs[R1] ← 32 024 ## Mem[40+Regs[R3]]
LH R1,40 (R3) 载入整型半字 Regs[R1] ← 32 (Mem[40+Regs[R3]]0)16 ## Mem[40+Regs[R3]]
## Mem[41+Regs[R3]]
LF F0,50 (R3) 载入单精度浮点 Regs[F0] ← 32 Mem[50+Regs[R3]]
LD F0,50 (R2) 载入双精度浮点 Regs[F0] ## Regs[F1] ← 64 Mem[50+Regs[R2]]
SW 500 (R4),R3 储存整型字 Mem[500+Regs[R4]] ← 32 Regs[R3]
SF 40 (R3),F0 储存单精度浮点 Mem[40+Regs[R3]] ← 32 Regs[F0]
SD 40 (R3),F0 储存双精度浮点 Mem[40+Regs[R3]] ← 32 Regs[F0]
Mem[44+Regs[R3]] ← 32 Regs[F1]
SH 502 (R2),R31 储存整型半字 Mem[502+Regs[R2]] ← 16 Regs[R31]16..31
SB 41 (R3),R2 储存整型字节 Mem[41+Regs[R3]] ← 8 Regs[R2]24..31
DLX中 Load和 Store指令实例
2,ALU操作
简单的算术和逻辑运算
寄存器比较指令 (?,?,?,?,?,?)
指令实例 指令名称 含 义
Add R1,R2,R3 加 Regs[R1] ← Regs[R2] + Regs[R3]
ADDI R1,R2,#3 和立即值相加 Regs[R1] ← Regs[R2] + 3
LHI R1,#42 载入高位立即值 Regs[R1] ← 42 ## 0 16
SLLI R1,R2,#5 逻辑左移的立即值形式
Regs[R1] ← Regs[R2] <<5
SLT R1,R2,R3 设置小于 if (Regs[R2] < Regs[R3])
Regs[R1] ← 1 else Regs[R1] ← 0
表 2- 13 ALU指令实例
3,分支和跳转操作根据描述目标地址的方法和是否链接可以将跳转操作指令分为四种类型 。
其中:
两种类型的跳转指令用带符号位的 26位偏移量加上程序计数器的值来确定跳转的目标地址;
另外两种类型的跳转指令则指定一个寄存器,
由寄存器中的内容决定跳转的目标地址 。
跳转有两种类型:
简单跳转跳转并链接 ( 用于过程调用 )
返回一个地址,也即将下一条顺序指令地址 ( 返回地址 ) 保存在寄存器 R31中 。
分支指令:
分支目标地址由一个带符号的 26位偏移量加上程序计数器的值来确定 。
指令实例 指令名称 含义
J name 跳转 PC ← name;
((PC+4)-225) ≤ name ≤((PC+ 4)+225)
JAL name 跳转并链接 Regs[R31] ← PC+4; PC ← name;
((PC+4)-225) ≤ name ≤((PC+ 4)+225)
JALR R2 寄存器型跳转并链接
Regs[R31] ← PC+4; PC ← Regs[R2];
JR R3 寄存器型跳转 PC ← Regs[R3];
BEQZ R4,
name
“等于 0”分支 if (Regs[R4]==0) PC ← name;
((PC+4)-215) ≤ name ≤((PC+ 4)+215)
BNEZ R4,
name
“不等于 0”分支 if (Regs[R4]!=0) PC ← name;
((PC+4)-215) ≤ name ≤((PC+ 4)+215)
表 2- 19 典型的分支和跳转指令
4,浮点操作在 DLX中,浮点指令的操作数来源于浮点寄存器,同时它还指明了相应的操作是单精度浮点操作还是双精度浮点操作 。
DLX所有指令及其含义:
表 2- 15 DLX中的所有指令及其含义指令类型操作码 含 义数 据传送
LB,LBU,SB 载入字节,载入无符号字节,储存字节
LH,LHU,SH 载入半字,载入无符号半字,储存半字
LW,SW 载入字,储存字
LF,LD,SF,SD 载入单精度浮点,载入双精度浮点,储存单精度浮点,储存双精度浮点
MOVI2S,MOVS2I 将通用寄存器中的内容移入特殊寄存器,将特殊寄存器中的内容移入通用寄存器
MOVF,MOVD 将一个单精度 /双精度浮点寄存器的内容拷贝到另一个单精度 /双精度浮点寄存器
MOVFP2I,MOVI2FP 将 32位浮点寄存器中的内容移入整型寄存器,
将 32位整型寄存器中的内容移入浮点寄存器指令类型操作码 含 义算术 /逻辑
ADD,ADDI,ADDU,ADD
UI
带符号加,带符号立即值加,无符号加,无符号立即值加
SUB,SUBI,SUBU,SUB
UI
带符号减,带符号立即值减,无符号减,无符号立即值减
MULT,MULTU,DIV,DIV
U
带符号乘,无符号乘,带符号除,无符号除
AND,ANDI 与,和立即值与
OR,ORI,XOR,XORI 或,和立即值或,异或,和立即值异或
LHI 载入高位立即值
SLL,SRL,SRA,SLLI,S
RLI,SRAI
包含了立即值 ( S_I) 和变量 ( S_) 的移位操作,移位有:逻辑左移,逻辑右移和算术右移
S_,S_I 设置条件,,_”可以是 LT,GT,LE,GE,EQ,NE
指令类型操作码 含 义控制
BEQZ,BNEZ 根据指定通用寄存器的内容等于 /不等于 0分支
BFPT,BFPF 测试浮点状态寄存器中的比较位为真 /
假进行分支
J,JR 跳转,基于寄存器的跳转
JAL,JALR 跳转并链接,基于寄存器的跳转并链接
TRAP 转换到操作系统
RFE 从异常恢复用户模式指令类型操作码 含 义浮 点
ADDD,ADDF 双精度浮点加,单精度浮点加
SUBD,SUBF 双精度浮点减,单精度浮点减
MULTD,MULTF 双精度浮点乘,单精度浮点乘
DIVD,DIVF 双精度浮点除,单精度浮点除
CVTF2D,CVTF2I,CVTD
2F,CVTD2I,CVTI2F,CV
TI2D
转换指令,CVTx2y表示从类型 x转换到类型 y,
其中 x和 y可以是 I( 整型 ),D( 双精度浮点 ),F( 单精度浮点 )
_D,_F 双精度浮点和单精度浮点比较,,_”可以是
LT,GT,LE,GE,EQ,NE,根据比较结果设置浮点状态寄存器中的位表 2- 16 基于 SPECint92基准程序集的指令使用频率测量统计结果指令 compress eqntott Espresso gcc(cc1) li 整型平均载入 19.8% 30.6% 20.9% 22.8% 31.3% 26%
存储 5.6% 0.6% 5.1% 14.3% 16.7% 9%
加 14.4% 8.5% 23.8% 14.6% 11.1% 14%
减 1.8% 0.3% 0.5% 0%
乘 0.1% 0%
除 0%
比较 15.4% 26.5% 8.3% 12.4% 5.4% 13%
载入立即值 8.1% 1.5% 1.3% 6.8% 2.4% 3%
指令 compress eqntott Espresso gcc(cc1) li 整型平均条件分支 17.4% 24.0% 15.0% 11.5% 14.6% 16%
无条件分支 1.5% 0.9% 0.5% 1.3% 1.8% 1%
调用 0.1% 0.5% 0.4% 1.1% 3.1% 1%
返回,跳转 0.1% 0.5% 0.5% 1.5% 3.5% 1%
移位 6.5% 0.3% 7.0% 6.2% 0.7% 4%
与 2.1% 0.1% 9.4% 1.6% 2.1% 3%
或 6.0% 5.5% 4.8% 4.2% 6.2% 5%
其它 ( 异或,
非 )
1.0% 2.0% 0.5% 0.1% 1%
指令 compress eqntott Espresso gcc(cc1) li 整型平均载入浮点数 0%
储存浮点数 0%
浮点加 0%
浮点减 0%
浮点乘 0%
浮点除 0%
浮点比较 0%
浮点寄存器移动
0%
其它浮点操作
0%
表 2- 17 基于 SPECfp92基准程序集的指令使用频率测量统计结果指令 doduc ear hydro2d mdljdp2 su2cor 整型平均载入 1.4% 0.2% 0.1% 1.1% 3.6% 1%
储存 1.3% 0.1% 0.1% 1.3% 1%
加 13.6% 13.6% 10.9% 4.7% 9.7% 11%
减 0.3% 0.2% 0.7% 0%
乘 0%
除 0%
比较 3.2% 3.1% 1.2% 0.3% 1.3% 2%
载入立即值 2.2% 0.2% 2.2% 0.9% 1%
指令 doduc ear hydro2d mdljdp2 su2cor 整型平均条件分支 8.0% 10.1% 11.7% 9.3% 2.6% 8%
无条件分支 0.9% 0.4% 0.4% 0.1% 0%
调用 0.5% 1.9% 0.3% 1%
返回,跳转 0.6% 1.9% 0.3% 1%
移位 2.0% 0.2% 2.4% 1.3% 2.3% 2%
与 0.4% 0.1% 0.3% 0%
或 0.2% 0.1% 0.1% 0.1% 0%
其它(异或,
非)
0%
指令 doduc ear hydro2d mdljdp2 su2cor 整型平均载入浮点数 23.3% 19.8% 24.1% 25.9% 21.6% 23%
储存浮点数 5.7% 11.4% 9.9% 10.0% 9.8% 9%
浮点加 8.8% 7.3% 3.6% 8.5% 12.4% 8%
浮点减 3.8% 3.2% 7.9% 10.4% 5.9% 6%
浮点乘 12.0% 9.6% 9.4% 13.9% 21.6% 13%
浮点除 2.3% 1.6% 0.9% 0.7% 1%
浮点比较 4.2% 6.4% 10.4% 9.3% 0.8% 6%
浮点寄存器移动
2.1% 1.8% 5.2% 0.9% 1.9% 2%
其它浮点操作
2.4% 8.4% 0.2% 0.2% 1.2% 2%
图2,1 4 指令使用频率的整型平均
0.00% 5.00% 10.00% 15.00% 20.00% 25.00% 30.00%
与移位或整型储存整型比较整型加条件分支整型载入
compress eqntott espresso gcc li
图2,1 5 指令使用频率的浮点平均
0.00% 5.00% 10.00% 15.00% 20.00% 25.00%
浮点载入浮点乘整型加浮点储存条件分支浮点加浮点减浮点比较浮点寄存器移动移位
doduc ear hydro2d mdljdp2 su2cor
2.7.2 DLX指令集结构效能分析
DLX指令集结构的指令格式,寻址方和操作都非常简单 。
图 2,1 6 V A X 8 7 0 0 和 M IP S M 2 0 0 0 的比较结果
0.00
0.50
1.00
1.50
2.00
2.50
3.00
3.50
4.00
s
p
i
c
e
m
a
t
r
i
x
n
a
s
a
7
f
p
p
p
p
t
o
m
a
c
t
v
doduc e
s
p
re
s
s
o
e
q
n
t
o
t
t
li
S P E C 8 9 基准程序
M
IP
S
/
V
A
X
性能比率指令执行比率
C P I 比率