MIT6828 学习笔记 013 (lab traps)
RISC-V 指令表
点击展开
- rs: register source
- rd: register destination
- lc: lui constant, 20-bit value
- l: label
- c: constant
- o: offset
- s: shift amount, unsigned, 5-bit
| 语法 | 描述 | 执行 |
|---|---|---|
lui rd, lc | Load Upper Immediate | rd=lc<<12 |
jal rd, l | Jump And Link | rd=pc+4;pc=label |
jalr rd, o(rs1) | Jump And Link Register | rd=pc+4;pc=rs1+o |
beq rs1, rs2, l | Branch if = | pc=(rs1==rs2?l:pc+4) |
bne rs1, rs2, l | Branch if != | pc=(rs1!=rs2?l:pc+4) |
blt rs1, rs2, l | Branch if < | pc=(rs1<rs2?l:pc+4) |
bltu rs1, rs2, l | Unsigned version | |
bge rs1, rs2, l | Branch if >= | pc=(rs1>=rs2?l:pc+4) |
bgeu rs1, rs2, l | Unsigned version | |
lb rd, o(rs1) | Load Byte | rd=mem[rs1+o] |
lbu rd, o(rs1) | Unsigned version | |
lh rd, o(rs1) | Load Half word | rd=mem[rs1+o+1:rs1+o] |
lhu rd, o(rs1) | Unsigned version | |
lw rd, o(rs1) | Load Word | rd=mem[rs1+o+3:rs1+o] |
sb rs2, o(rs1) | Store Byte | mem[rs1+o]=rs2[7:0] |
sh rs2, o(rs1) | Store Half Word | mem[rs1+o+1:rs1+o]=rs2[15:0] |
sw rs2, o(rs1) | Store Word | mem[rs1+o+3:rs1+o]=rs2 |
addi rd, rs1, c | ADD Immediate | rd=rs1+c |
slti rd, rs1, c | Compare < Immediate | rd=(rs1<c?1:0) |
sltiu rd, rs1, c | Unsigned version | |
xori rd, rs1, c | XOR Immediate | rd=rs1^c |
ori rd, rs1, c | OR Immediate | rd=rs1|c |
andi rd, rs1, c | AND Immediate | rd=rs1&c |
slli rd, rs1, s | Shift Left Logical Immediate | rd=rs1<<s |
srli rd, rs1, s | Shift Right Logical Immediate | rd=rs1>>s |
srai rd, rs1, s | Shift Arithmetic Immediate | rd=rs1>>s |
add rd, rs1, rs2 | ADD | rd=rs1+rs2 |
sub rd, rs1, rs2 | SUBtract | rd=rs1-rs2 |
sll rd, rs1, rs2 | Shift Left Logical | rd=rs1<<rs2[4:0] |
slt rd, rs1, rs2 | Set Less Than | rd=(rs1<rs2?1:0) |
sltu rd, rs1, rs2 | Unsigned version | |
xor rd, rs1, rs2 | XOR | rd=rs1^rs2 |
srl rd, rs1, rs2 | Shift Right Logical | rd=rs1>>rs2[4:0] |
sra rd, rs1, rs2 | Shift Right Arithmetic | rd=rs1>>rs2[4:0] |
or rd, rs1, rs2 | OR | rd=rs1|rs2 |
and rd, rs1, rs2 | AND | rd=rs1&rs2 |
RISC-V 伪指令表
点击展开
| 语法 | 描述 | 执行 |
|---|---|---|
li rd, c | Load Immediate | rd=c |
mv rd, rs1 | MoVe | rd=rs1+0 |
not rd, rs1 | NOT | rd=rs1^-1 |
neg rd, rs1 | Negation | rd=0-rs1 |
j l | Jump | pc=l |
jal l, call l | Jump and link | ra=pc+4;pc=l |
jr rs1 | Jump Register | pc=rs1&~1 |
jalr rs1 | Jump And Link Register | ra=pc+4;pc=rs1&~1 |
ret | RETurn from subroutine | pc=ra |
bgt rs1, rs2, l | Branch > | pc=(rs1>rs2?l:pc+4) |
bgtu rs1, rs2, l | Unsigned version | |
ble rs1, rs2, l | Branch <= | pc=(rs1<=rs2?l:pc+4) |
bleu rs1, rs2, l | Unsigned version | |
beqz rs1, l | Branch == 0 | pc=(rs1==0?l:pc+4) |
bnez rs1, l | Branch != 0 | pc=(rs1!=0?l:pc+4) |
bltz rs1, l | Branch < 0 | pc=(rs1<0?l:pc+4) |
bgez rs1, l | Branch >= 0 | pc=(rs1>=0?l:pc+4) |
bgtz rs1, l | Branch > 0 | pc=(rs1>0?l:pc+4) |
blez rs1, l | Branch <= 0 | pc=(rs1<=0?l:pc+4) |
记得 checkout 到 traps
1. RISC-V 汇编
读 user/call.c 生成的 user/call.asm 。问:函数参数放在了那个寄存器?比如 user/call.c:15 里给 printf 传的 13 放在了那个寄存器?
答:粗略的说先放进参数寄存器
a0-a7,放不下的放进栈里,具体在 RISC-V 调用约定里有讲;根据user/call.asm:45 可知13放进了a2里。
问: main 调用 f 的汇编代码在哪? g 呢?
答:根据
user/call.asm:26 可知调用被优化了,直接在编译期得出了f(8)+1为12,所以main没有调用f或g。
问: printf 函数的地址?
答:根据
user/call.asm:1078 可知printf的地址为0x0000000000000630(0x630)
问: main jalr 到 printf 之后 ra 的值?
答:
jalr产生的变化是ra = pc + 4; pc = rs1 & ~1所以根据user/call.asm:50 可知jalr之后ra为pc + 4 = 34 + 4 = 38
问:因为 RISC-V 采用 little-endian ,下面的代码会输出 Hello World:
1 | unsigned int i = 0x00646c72; |
如果在 big-endian 情况下,想要这段代码输出 Hello World , i 应该设为什么值? 57616 需要改吗?
答:
i应该设为0x726c6400,57616不用改
问:下面的代码中 y= 后面打印的值是什么?为什么?
1 | printf("x=%d y=%d", 3); |
答:打印的值应该是寄存器
a2的值,因为调用约定规定参数先通过a0-a7传,传给printf后a0应该指向"x=%d y=%d"字符串、a1值为3、剩下的寄存器是未定义的,然后实际打印出来的应该就是未定义的寄存器a2的值
2. Backtrace
通过 fp (s0) 寄存器保存的栈帧地址打印进程的 backtrace ,格式像这样:
1 | backtrace: |
通过 addr2line 可以获取对应 C 代码位置:
1 | .../kernel/sysproc.c:58 |
点击展开:我的答案
1 | diff --git a/kernel/defs.h b/kernel/defs.h |
3. Alarm
设计两个系统调用,用户进程通过 int sigalarm(int ticks, void(*handler)()); 格式的系统调用每隔 ticks (设为 0 取消)使用户进程进入 handler , handler 内通过系统调用 int sigreturn(void); 使用户进程返回进入 handler 之前的位置,换句话说就是实现一个类似 trap 的机制。
点击展开:我的答案
1 | diff --git a/kernel/proc.c b/kernel/proc.c |
题目让我思考 trap 进 handler 之前应该保存哪些寄存器来确保用户代码可以正确地返回之前的位置,我只想到
pc肯定得存,但别的寄存器我实在不知道哪个不需要存,索性直接把所有寄存器都存了。