Chapter 2 指令¶
介绍¶
指令类型
共3种
- Register-Register
- 最多有3个操作数(operands),不含内存地址
- Register-memory
- 只有两个操作数,只有一个内存地址
- Memory-memory
- ⅔个操作数,内存地址可以是2到3个
变量类型
寄存器,内存地址,堆栈
硬件操作¶
算数操作:每次只有一个操作符,恰好三个变量(有且仅有)
寄存器操作数¶
算数操作的操作数必须是寄存器或者immediate内存
RISC-V 中32 个寄存器,每个64位。体现了设计思想2,越少越快。
64位,称为doubleword
RISC-V中32个寄存器分别代表的意义(公约convention)
操作数
内存一个占8位,因为byte address。
内存操作数¶
优点 更多数据,更复杂的数据结构
数据传输指令 ld(load doubleword) 将内存传到寄存器,sd(store doubleword) 寄存器到内存
RISC-V中的特点 一个 \(8\) 位,小端(little Endian) 存储。对于一个32位机子上存放0x12345678,需要4个地址,地址小的存放位权小的叫小端存储。
内存对齐 memory alignment
内存指令举例
其它的都很好理解,就是 64(x22) 。64 相当于x0x86那里学过的offset,那里是 segment:offset,这里相当于x22就是segment,A[0] 的位置, A[8] 的位置就是x22+8*8 ,那就是可以写成64(x22)
注意之前说过的原则只有先寄存器再内存,所以就算是sd也是先写寄存器x9然后是内存96(x22)
内存和寄存器比较¶
寄存器相比于内存更快,内存需要ld,sd,所以要更多操作编译器尽可能多的使用寄存器作为变量。
内存的调用的话经常会使用到关于某个寄存器的偏移地址(比如x3,全局指针的位置,毕竟要预留堆栈位置)
常数使用¶
一种是知道存储某个常数的地址。
另一种是 addi x22,x22,4
最后一位写常数。 这里的i表示 immediate
有符号和无符号数¶
就是2’s-complement表示下,若符号位为负,则表达的数为 - (只看其余位置,取反加1),非负则表达数不变。
得到补码也取反加1。
符号填充:将凭空出来的高位填为符号位,表达的数不变。
-
lb: sign-extend loaded byte
-
lbu: zero - extend loaded byte
表示计算机指令¶
叫做机器码,二进制表示,寄存器x0-x31用0-31表示,应该指令总共是32位
opcode,一个较小的数对应什么操作
RISC-V没有非,因为操作数与其它操作相比,只有一个,太少,我们通过与1异或来实现它
例子¶
具体格式¶
rd,就是说你要存到哪去,就是例子中的x9
逻辑操作¶
- 左移
与或非的机器码没有列出来
分支操作¶
L1是一个标签。打上标签时就再开头写一下就可以了。然后出现在分支语句中。
beq表示如果等于跳转 equal
bne表示不等就跳转 ne
blt 表示算数小于就跳
bge 表示great or equal(算数大于等于)
bltu和bgeu都是表示相应的无符号数操作。
书上还提到边界检查的简单方法
原本有符号数(例如数组下标) \(x22\) 判断是否超出了边界 \(x10\) 或小于 \(0\) ,我们要写两个b开头的汇编。但是你会发现x10必定是正的(数组下标上界),那么当 x22小于 \(0\) 时,用无符号数判断时也是会超出 \(x10\) 的,因为 \(1\) 打头,表负数。
注意:x10,x22都是一堆二进制,怎么看它是有符号还是无符号你自己定义的,看你用什么指令。