整数运算指令
格式为:[指令名] [目标], [源], [操作数]
例如:
add t0, t1, t2
意为将t1 + t2存放入t0
xori t0, t1, 0x7EF
意为将t1 ^ 0x7EF存放入t0
注意: RV32I指令长度为32bit,带立即数的运算指令(I型指令)的立即数只有12bit,因此带符号数只能取-0x800到0x7FF之间的数
具体地,运算指令有以下种类:
- 算术运算:
addsub - 逻辑运算:
andorxor - 移位运算:
sllsrlsra
访存指令
格式为:
[指令名(读)] [目的寄存器] [偏移量]([地址寄存器])
[指令名(写)] [源寄存器] [偏移量]([地址寄存器])
例如:
lb t0, (0x8)t1
意为将t1寄存器中的地址加0x8后得到的地址处的1个字节存入t0
具体地,有:
- 读指令:
lwlhlb - 无符号读指令:
lhulbu - 写指令:
swshsb
比较指令
slt a0, a1, a2
意为若a1 < a2则将a0置1,否则为0,sltu为对应的无符号数比较版本;slti sltiu为对应的立即数版本
例如以下代码段:
1 | addi a0, a0, 0x123 |
运行后a2为0,a3为1
长立即数操作指令
lui a4 0x4daf1
将a4的高20位设为0x4daf1
auipc a5 0x6789a
将a5设为PC + 0x6789a000
条件分支指令
beq a0, a1, equal
若a0 == a1则跳转至equal标号处
还有bne bge bgeu blt bltu
无条件跳转
jal s9, target
跳转至target,将当前的PC + 4(即下一条指令地址)存入s9
使用jal x0, target则只会进行跳转,因为x0不可改变
jalr x0, x1, 0可以实现ret,即跳转到x1 + 0(x1用来存储返回地址)
类似地,jal x1, func可以实现过程调用(注意:这里没有维护栈帧)
杂项指令
看不懂,下一个 :(
伪指令
RV32I使用了一些方便用户的伪指令,其本质使用其他指令实现,例如ret的真实实现是jalr x0, x1, 0
一定要善用伪指令和标号~
RV32I函数使用惯例
寄存器使用
a开头为函数参数寄存器,调用者保存s开头为被调用者保存寄存器t开头为临时寄存器,调用者保存
函数格式
函数入口:
1 | entry_label: |
函数结尾:
1 | lw ra, framesize-4(sp) # 从栈帧读取返回地址 |