计算机指令
算术运算指令
add a, b, c# a = b + c
设计原则一——对指令进行规整化设置
简化实现
获得更高的性能,更低的成本
代码示例
C语言代码
f = (g + h) - (i + j)
MIPS
add t0, g, h # temp : t0 = g + h
add t1, i, j # temp : t1 = i + j
sub f, t0, t1 #final: f = t0 - t1
参与算术逻辑运算的变量必须是寄存器变量,对于MIPS(x 32)指令集来说,有32个寄存器,每个寄存器长32 b i t 32bit32bit,它们:
用于存储最常用到的变量
每个寄存器的编号为0~31
每个寄存器中32位数据称作一个字
约定符号:
$t0,$t1,…,$t9 用于表示临时变量
$s0,$s1,…,$s 7表示需要保存的变量
设计原则二——更小就会更快
倾向于将算术运算放到寄存器中运行,以加快速度
内存的存储单元通常是百万级的
前面的代码,根据约定符号进行矫正:
add $t0, $s1, $s2
add $t1, $s3, $s4
sub $s0, $t0, $t1
可以看到,在M I P S 32 MIPS32MIPS32指令集中,只有32 ∗ 32 32*3232∗32个存储空间,这并不足以支持所有运算,所以很多的数据是存储在内存中的,我们将这些被储存在内存中的数据称为_Memory Operands。内存的操作数是用来保存复杂的操作数,这是由寄存器的空间过小导致的。
算数逻辑运算不能直接对内存中的数据进行运算,这就需要我们先从内存中读取(load)数据,然后再进行处理,最终再将操作的结果写入(write)内存。在对内存的操作中,都是通过字节寻址的(每八位分配一个地址)。值得注意的是,每个字节由8 b i t 8bit8bit组成,而寄存器中每一位长度是32 b i t 32bit32bit,这就表明,我们在知道首地址的情况下,想要寻找第i ii个元素时,需要将偏移量∗ 4 *4∗4。
MIPS中数据的对齐方式是:大端对齐。什么是大端对齐呢?
存储一个数据,需要四个字节,这也就对应着内存上的四个存储单元。
这四个存储单元有他们相应的标号
高内存地址放整数的低位,低内存地址放整数的高位,这种方式叫正着放,术语叫大端对齐
与大端对齐相反的是小端对齐,说白了就是反着存
Memory Operand 小例子
C code
g = h + A[8]
其中,$s1 is g,$s2 is h,$s3 is the base address of A
MIPS code
lw $t0, 32($s3) # 读取A[8],32来源于偏移量*4 = 8*4 = 32
add $s1, $s2, $t0
Memory Operand 小例子2
C code
A[12] = h + A[8]
其中,$s2 is h,$s3 is the base address of A
MIPS code
lw $t0, 32($s3)
add $t0, $s2, $t0
sw $t0, 48($s3)#写入
可以看出,寄存器和内存的交互主要通过l w lwlw ,s w swsw两条语句,进行数据的读取、写入,对于编译器而言,选择哪些变量放到寄存器中,哪些放到内存中,是非常关键的问题,同时也是非常困难的问题。
立即数
所谓立即数,即所使用的变量是一个常数,他是包含在指令中的。如:
addi $s3, $s3, 4
这就相当于C语言中的
s3 += 4;
立即数操作中没有减法,因为减掉一个数就相当于加上这个数的相反数,即:
addi $s3, $s3, -4
就可以完成C语言中如下功能
s3 -= 4;
**支持的数字范围:**立即数操作的常亮仅支持有符号的16位整数,即:[ − 32768 , 32767 ] \left[ -32768, 32767\right][−32768,32767]这个区间。如果需要一个32位整数,那么这个整数就只能被先放到内存中,然后再被寄存器读取。
这里就引出了第三个设计原则:加快高概率事件(Make the Common Case Fast)
小的常数是常用的
使用常用的数,不需要从内存中读取
数