CS61C 学习笔记
RISC-V Assembly Language
RISC-V 是一种指令集

这幅图展现了处理器的基本架构
接下来介绍一下 RISC-V 的指令,
加法命令
add x1, x2, x3
- x1 表示结果的位置
- x2 表示第一个操作数的位置
- x3 表示第二个操作数的位置
减法运算
sub x3, x4, x5
如果有算式
d = e - f
其中 d 是 x3,e 是 x4,f 是 x5
如果我现有 C 语言算式 f = (g + h) - (i + j)
把它翻译成 RISC-V 就是
add x5, x20, x21 # a_tmp = g + h
add x6, x22, x23 # b_tmp = i + j
sub x19, x5, x6 # f = (g + h) - (i + j)
立即加法指令
addi x3, x4, 10
和
f = g + 10
是一样的,其中 x3 是 f,x4 是 g
一般,我们如果要复制一个寄存器的值,要用指令
add x3, x4, x0
因为 x0 恒为 0

Load from memory 就是数据从内存中加载进寄存器中
Store to memory 就是数据从寄存器放回内存中
数据通常小于 32 位,但很少小于 8 位
所以,我们把 8 位一组称为一个字节
地址实际上是按照字节计算的例如,32 位就占 4 个字节
小端序约定表示低的地址位对应于低的字节位

回到 RISC-V 指令
现有 C 语言代码
int A[100]
g = h + A[3]
使用 RISC-V 写就是
lw x10, 12(x15) # x10 = A[3]
add x11, x12, x10
- x15 就是
A[0]的位置 - 12 就是偏移量,3 个字节
int A[100]
A[10] = h + A[3]
使用 RISC-V 去书写就是
lw x10, 12(x15)
add x10, x12, x10
sw x10, 40(x15)
除了加载 4 个字节的 sw,lw 以外,我们有可以加载 1 个字节的 sb,lw
在 RISC-V 中,if 语句的指令是
beq reg1, reg2,L1
如果寄存器 reg1 中的值等于寄存器 reg2 中的值,则跳转到标记为 L1 到语句,否则,进入下一条语句
beq 到意思是 branch if equal
除了
beq 之外还有一些条件控制语句
- beq (branch if equal)
- bne (branch if not equal)
- blt (branch if less than)
- bge (branch if greater)
- bltu, bgeu 是 blt 和 bge 的无符号版本
还有一个无条件分支,总是跳转
- jump(j) label
看一个例子
f->x10 g->x11 h->x12 i->x13 j->x14
if (i == j)
f = g + h
else
f = g - h
翻译成汇编码就是
bne x13, x14, Else
add x10, x11, x12
j Exit
Else: sub x10, x11, x12
Exit:
C 语言中有三种循环类型,while,for 和 do while
每种形式可以重写成另外两种之一,因此,相同的分支方法可以应用于这些所有循环
来看一下 C 语言循环转化到 RISC- V 汇编的例子

RISC-V 中也有按位处理的操作,被称为逻辑操作

这些操作始终有两种变体
- 寄存器形式
add x5, x6, x7 - 立即数形式
addi x5, x6, 3 # x5 = x6 & 3
在 RISC-V 中没有 not 指令,所以需要用与 11111111 取 xor 操作来代替
移位分为两种,一种是逻辑移位(sll)和立即数形式(slli)
slli x1, x12, 2 # x11 = x12 << 2
将 x12 的值左移 2 位,超出部分丢弃,右侧补零
还有一种是算数移位,算数右移(sra,srai)向右移动 n 位,将最高位符号位填入空出的位置
还有一些有用的 RISC-V 汇编器特性
例如 a0-a7 表示函数调用时的参数寄存器 x10-x17
还有一些伪指令,常见汇编用法的简写语法
mv rd, rs = addi rd, rs, 0
li rd, 13 = addi rd, x0, 13
nop = addi, x0, x0, 0
Function Calls
调用函数有六个基本步骤
- 将参数(arguments)放在函数可以访问的位置
- 将控制权转移到函数
- 获取函数所需的(本地)存储资源
- 执行函数的任务
- 将返回值放在调用函数可以访问的位置,并恢复函数使用的的寄存器,释放局部存储空间
- 将控制权返回到控制点,因为一个函数可以从程序中的多个位置被调用
RSIC-V 函数调用约定
- 寄存器比内存更快,因此,尽量使用寄存器
- a0-a7 (x10-x17) 八个参数寄存器,用于传递参数和两个返回值(a0-a1)
- ra(x1):一个返回地址寄存器,用于返回调用点
- 还有 s0-s1(x8-x9) 和 s2-s11(x18-x27)保存寄存器
在 RSIC-V 中,所有指令都是 4 个字节,并像数据一样存储在内存中

在调用函数的位置有两步
1008 addi ra, zero, 1016
1012 j sum
这里可以写成一步
1008 jal sum
表示 jump and link 链接并跳转
从函数返回的时候,无条件的跳转到寄存器 ra 的位置
j ra
也可以写成伪指令的形式
ret