UCB CS61C:计算机架构的伟大思想
RISC-V
RISC 哲学:
- 保持指令集小而简单,让其能更容易构建快速的硬件
- 通过将简单的组合在一起让软件实现复杂的操作
RISC-V 中有 32 个寄存器
- 序号为 x0-31
- x0 的值总是为 0
- 可以使用序号引用,也可以使用名字
注释 #
,只支持单行注释
加减的基本格式:sub x3, x4, x5
等价于 x3 = x4 - x5
立即数有不同的指令:addi x3, x4, 10
,即最后一个数必须为立即数。注意到没有对应的减法的版本,因为可以通过加法实现
因为 0 非常常用,所以 x0 寄存器在硬件上设置为 0,即例如 add x0, x3, x4
的指令无效
从内存中读取数据:lw x10, 12(x15)
,其中 x15 指向 int 数组 A,则 x10 = A[3],即 12 表示字节的偏移
将数据存到内存中:sw x10, 40(x15)
除了按 word 转移数据,也支持按字节 byte 转移数据,如 lb
、sb
,注意 lb x10, 3(x11)
将内存中的数据复制到 x10 的低位中,使用符号扩展前面的内容
也有 unsigned 版本 lbu
,即使用 0 填充高位
分支指令:beq reg1, reg2, L1
表示如果 reg1 中的值 == reg2 中的值,则跳转到 L1 标签处,否则继续执行下一条指令
当然条件跳转还有其他版本,无条件跳转的指令为 j label
逻辑操作包括:and
、or
、xor
、sll
、srl
,注意没有逻辑 NOT,对 11111… 使用 xor 即可
一些常用指令的简写:
mv rd, rs = addi rd, rs, 0
li rd, 13 = addi rd, x0, 13
nop = addi x0, x0, 0
函数调用:
- a0-a7(x10-17) 用于传参,a0-a1 用于返回值
- ra(x1) 存储返回地址
- s0-s1(x8-x9) 和 s2-s11(x18-x27) 保存寄存器
- sp(x2) 是栈指针
被调函数返回使用 jr ra
之所以多了 r 是因为目标地址是一个变量
跳转并保存返回地址:jal 函数标签
(jump and link)
- 在函数调用中保持不变的变量保存在 s0-s11 中,用 callee 保存
- 会变的,如 a0-a7, t0-t6 由 caller 保存
寄存器 | ABI 名字 | 描述 | 保存者 |
---|---|---|---|
x0 | zero | 硬线的零 | |
x1 | ra | 返回地址 | caller |
x2 | sp | 栈指针 | callee |
x3 | gp | 全局指针 | |
x4 | tp | 线程指针 | |
x5 | t0 | 临时/交替链接寄存器 | caller |
x6-7 | t1-2 | 临时 | caller |
x8 | s0/fp | 保存寄存器/栈帧指针 | callee |
x9 | s1 | 保存寄存器 | callee |
x10-11 | a0-a1 | 函数参数/返回值 | caller |
x12-17 | a2-a7 | 函数参数 | caller |
x18-27 | s2-11 | 保存的寄存器 | callee |
x28-31 | t3-6 | 临时 | caller |
算术/逻辑 | 立即数 | 分支/跳转 | 载入/保存 |
---|---|---|---|
add rd, rs1, rs2 |
addi rd, rs1, rs2 |
beq rs1, rs2, Label |
lw rd, rs1, imm |
sub rd, rs1, rs2 |
subi rd, rs1, rs2 |
bne rs1, rs2, Label |
lb rd, rs1, imm |
and rd, rs1, rs2 |
andi rd, rs1, rs2 |
bge rs1, rs2, Label |
lbu rd, rs1, imm |
or rd, rs1, rs2 |
ori rd, rs1, rs2 |
blt rs1, rs2, Label |
sw rd, rs1, imm |
xor rd, rs1, rs2 |
xori rd, rs1, rs2 |
bgeu rs1, rs2, Label |
sb rd, rs1, imm |
sll rd, rs1, rs2 |
slli rd, rs1, rs2 |
bltu rs1, rs2, Label |
|
srl rd, rs1, rs2 |
srli rd, rs1, rs2 |
jal rd, Label |
|
sra rd, rs1, rs2 |
srai rd, rs1, rs2 |
jalr rd, rs, imm |
RISC-V 指令格式
每条指令都由 32 bit 组成
**R-Format:**寄存器之间的算术操作
- 其中 opcode 指明是哪一类型的指令,如 0110011 表示 R-Format 指令
- funct7 + funct3:和 opcode 共同决定是具体是哪个操作
- rs1, rs2, rd:表示具体哪个寄存器(x0-x31)
**I-Format:**寄存器和立即数之间的算术操作和装载
和 R-format 很像,只有 funct7 + rs2 合并成了立即数,注意到其只能表达 [-2048, 2047] 之间的数,同时立即数在算术操作中总是符号扩展到 32 位
注意到位移指令只使用了低 5 位,即只能移动 0-31 bit
**S-Format:**存储
**B-format:**分支
跳转时使用 PC 相关地址,同时为了兼容 16 bit 的压缩 RISC-V 指令,每次调整不是 4 字节,而是 2 字节,即 PC = PC + immediate*2
**U-format:**立即数指令的高 20 bit
- lui:装载立即数高位
- auipc:把立即数高位加到 PC 上
注意加法是符号扩展的,所以有时需要高位 +1
**J-format:**跳转
JAL 在 rd 寄存器中保存返回地址
jalr rd, rs, immediate 设置 PC = rs + immediate,注意不乘 2 字节