指令:计算机的语言

第2章 指令:计算机的语言

2.1 引言

计算机语言中的单词称为指令,其词汇表称为指令系统(instruction set)。

指令系统:被一个给定体系结构所理解的命令词汇表。

本书所选指令系统为RISC-V,2010年由加州大学伯克利分校开发。

两个流行的指令系统:

1.MIPS设计与1980年的指令系统典范。

2.Intel x86起源于20世纪70年代,现在仍支持PC以及后PC云端。

存储程序概念:指令与多种类型的数据不加区别地存储在存储器中并因此易于更改,因此产生了存储程序计算机。

RISC-V 操作数

名称

示例

注解

32个寄存器

x0~x31

快速定位数据,在RISC-V中,只对寄存器中的数据执行算术运算,寄存器x0总是等于0.

2^30个存储字

Memory[0],Memory[4],...,Memory[4294967292]

只能被数据传输指令访问。RISC-V使用字节寻址,因此顺序字访问相差4,存储器保存数据结构、数组和换出的寄存器内容。

RISC-V 汇编语言

类别

指令

示例

含义

注解

算术运算

立即数加

2.2 计算机硬件的操作

每台计算机必须能够实现算术运算,RISC-V汇编语言的符号 add a,b,c,指示计算机将两个变量b和c相加并将其总和放入a.

每个算术指令只执行一个操作,并且必须总是只有三个变量。加入b,c,d,e的和放入变量a中,指令序列将变成四个相加:

add a, b, c
add a, a, d
add a, a, e

硬件简单设计原则:操作数数量可变的硬件比固定数量的硬件更复杂。

设计原则1:简单源于规整。

例题:将两条C赋值语句编译成RISC-V

a = b + c;
d = a - e;

编译器将C语言转换成RISC-V汇编指令

add a, b, c
sub d, a, e

例题:将一条复杂的C赋值语句编译成RISC-V

f = (g + h) - (i + j);

编译器将C语言转换成RISC-V汇编指令

add t0, g, h
add t1, i, j
sub f, t0, t1

为了提高可移植性,Java最初被设计为依赖于软件的解释器。这个解释器的指令系统称为Java字节码(bytecode)。为了使性能接近于等效的C程序,现在的Java系统通常会将Java字节码编译为像RISC-V这样的机器指令,因此这样的Java编译器通常称为即时(立即,当下,立刻)编译器(JIT)。

2.3 计算机硬件的操作数

算术指令的操作数会收到限制,它们必须取自寄存器,而寄存器数量有限并内建于硬件的特殊位置。

寄存器是硬件设计中的基本元素,对程序员可见,将其视为计算机构建的"砖块"。

RISC-V体系中,寄存器大小为32位。

字:计算机中的一种基本访问单元,通常是32位一组,对应于RISC-V体系结构中单个寄存器的位宽。

双字:计算机中另一种基本访问单元,通常是64位一组。

当前RISC-V等计算机上的寄存器通常为32个,算术指令的三个操作数必须从32个32位寄存器中选择。

设计原则2:更少则更快。

寄存器编号:x0~x31

将案例 f = (g + h) - (i + j); 中的变量f、g、h、i、j分别分配给x19、x20、x21、x22、x23,编译后代码,

两个临时变量用x5和x6代替:

add x5, x20, x21
add x6, x22, x23
sub x19, x5, x6

2.3.1 存储器操作数

更复杂的数据结构,数组和结构体,包含比寄存器数量更多的数据元素,可以将其保存在内存中。

内存只是一个大型一维数组,其地址作为数组下标,从0开始,每个元素1Byte也就是8bit。

寄存器一次处理一个字也就是32位的数据,一次处理4个内存单元。

数据传输指令:在内存和寄存器之间传送数据的命令。

地址:用于描述内存数组中特定数据元素位置的值。

载入指令(load):将数据从内存复制到寄存器的数据传输指令通常称为载入指令。

取字的指令:lw

例题:当操作数在内存中时,编译C赋值语句

假设A是一个由100个字组成的数组,并且编译器和之前一样将寄存器x20和x21分别分配给变量g和h。我们还假设数组的起始地址或基址

存放在寄存器x22中,编译这个C赋值语句:

g = h + A[8]

答案:虽然赋值语句只有一个操作,但其中一个操作在内存中,我们必须将A[8]传送到一个寄存器。该数组元素的地址是数组A的基址(x22中)加上元素序号8的和。数据应该放在一个临时寄存器中以便下一条指令使用。

ld x9, 8(x22)//获取A[8]到x9中

下一条指令可以对x9操作,因为A[8]已存放在寄存器x9中。该指令必须将h(x21)和A[8](x9)相加,并将该和放入与g相对应的寄存器x20中:

add x20, x21, x9;//g = h + A[8]

存放基址寄存器x22被称为基址寄存器,而数据传输指令的常数8称为偏移量。

所有体系结构都是按单个字节寻址的,因此字地址与4个字节地址之一相匹配,连续字的地址相差4.

大端:最左边作为字地址。

小端:最右边作为字地址,RISC-V属于这小端。

为了上面代码获得正确的字节地址,加到基址寄存器x22的偏移量必须是8*4,以便取值地址将选择A[8]而不是A[8/4]。

存储指令(store):与载入指令相反,它从寄存器复制数据到内存,sw表示存储字。

对齐限制:数据在内存中要与自然边界对齐的要求,MIPS要求字的起始地址必须是4的倍数。

由于加载和存储指令中的地址是二进制数,我们可以看到为什么作为主存的DRAM以二进制而不是十进制表示容量大小。

也就是说以2的30次,40次方,而不是10的9次,12次方。

例题:使用load和store编译生成指令

A[12] = h + A[8];//h放入x21,A的基址放在x22
//汇编如下
lw x9, 32(x22)//将A[8]值载入x9
add x9, x21, x9//x9 = h + A[8]
//A[12]偏移量是48
sw x9, 48(x22) //将结果保存在A[12]中

硬件/软件接口

许多程序有着比计算机中寄存器数量更多的变量,所以,编译器会尽量把最常用的变量存放在寄存器中,剩下的存放在内存中,使用load和store在寄存器和内存之间传输变量,将不常用的变量存放到内存的过程称为寄存器换出。

寄存器与内存相比,快200倍(50ns与0.25ns),能效提高10000倍(1000pJ与0.1pJ),巨大差异导致缓存的出现。

2.3.2 常数或立即数操作

立即数加:其中一个操作数是常数,写为 addi x22, x22, 4 //x22=x22+4

RV64:64位宽的寄存器,是RISC-V的变体。

32位到64位地址变化让编译器编写者不得不考虑C语言中数据类型的大小。

操作系统

指针

int

long int

long long int

Microsoft Windows

64位

32位

32位

64位

Linux和大部分Unix

64位

32位

64位

64位

2.4 有符号数与无符号数

二进制数位:也称作位,以2为基数表示,或0或1,作为信息的基本单位。

推广到任意基数,第i个数位d的值是 d x 基的i次方,其中i从0开始从右向左递增。

1011 = 11

32位宽

最低有效位:字中上图中最右边的位,第0位。

最高有效位:字中最左边的位。

无法符号数,最大可以表示42亿多的数。

有符号数如下:

原码:计算机程序可以计算正数和负数,增加单独符号位来表示正负,有缺点,被放弃了。

二进制补码:前导0表示正数,前导1表示负数。

正数:0~2的31次-1(0~1000...0000),负数:-2的31次到-1(1000...0001到1111...1111)

-2的31次方没有正数。

无符号字节载入:lbu将字节视为无符号数,用0拓展填充寄存器最左位,C几乎总是使用字节来表示字符而不是将字节视为有符号的短整数。

字节载入:lb载入带符号整数。

二进制补码求反:将1转为0,将0转为1,结果加1.

符号拓展:将n位表示的二进制转换为一个多余n位表示的数,先取位数更少的书的最高位(符号位),将其复制填充位数更多的数的新位,原来的非符号位被复制到新字的右侧。

2.5 计算机中的指令表示

指令以一系列高低电平信号的形式保存在计算机中,并且以数字的形式表示。每条指令的各个部分都可以被视为一个单独的数,把这些数字并排拼到一起便形成了指令。32个寄存器也只是用0到31这些数来表示。

例题:将一条RISC-V汇编指令翻译为一条机器指令

add x9, x20, x21

十进制表示为:

0

21

20

0

9

51

一条指令的每一段称为一个字段,第1,4,6(0,0,51)组合起来告诉计算机该指令执行加法操作。

第2个字段给出加法运算的第2个源操作数寄存器21号,第3个字段给出另一个源操作数寄存器x20,第5个将相加之和存放在x9。

0000000

10101

10100

000

01001

0110011

7位

5位

5位

3位

5位

7位

总数32位。

指令格式:由二进制数字字段组成的指令表示形式。

机器语言:用于计算机系统内通信的二进制表示。

这样的指令序列称作机器码。

十六进制:以16为基数的数字,由于二进制字串冗长,而且几乎所有计算机数据大小都是4的倍数,所以每4位替换一位十六进制。

十六进制-二进制转换表

十六进制

二进制

十六进制

二进制

0

0000

8

1000

1

0001

9

1001

2

0010

a

1010

3

0011

b

1011

4

0100

c

1100

5

0101

d

1101

6

0110

e

1110

7

0111

f

1111

经常处理不同进制,十进制下标10,二进制下标2,十六进制下标16。

C和Java用0xnnnn来表示十六进制。

RISC-V 字段

给字段命名使其更易于讨论:

funct7

rs2

rs1

funct3

rd

opcode

7位

5位

5位

3位

5位

7位

RISC-V指令中每个字段名称的含义:

  • opcode(操作码):指令的基本操作,这个缩写是它的惯用名称。
  • rd:目的操作数寄存器,用来存放操作结果。
  • funct3:一个另外的操作码字段。
  • rs1:第一个源操作数寄存器。
  • rs2:第二个源操作数寄存器。
  • funct7:一个另外的操作码字段。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阿立聊全栈

有作用的,有闲钱的支持一点。

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值