Skip to content

RISC-V assembly (easy)

在虚拟机终端运行

bash
$ make fs.img

阅读在user/call.asm中生成可读的汇编版本

asm
0000000000000000 <g>:
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"

int g(int x) {
   0:	1141                	addi	sp,sp,-16
   2:	e422                	sd	s0,8(sp)
   4:	0800                	addi	s0,sp,16
  return x+3;
}
   6:	250d                	addiw	a0,a0,3
   8:	6422                	ld	s0,8(sp)
   a:	0141                	addi	sp,sp,16
   c:	8082                	ret

000000000000000e <f>:

int f(int x) {
   e:	1141                	addi	sp,sp,-16
  10:	e422                	sd	s0,8(sp)
  12:	0800                	addi	s0,sp,16
  return g(x);
}
  14:	250d                	addiw	a0,a0,3
  16:	6422                	ld	s0,8(sp)
  18:	0141                	addi	sp,sp,16
  1a:	8082                	ret

000000000000001c <main>:

void main(void) {
  1c:	1141                	addi	sp,sp,-16
  1e:	e406                	sd	ra,8(sp)
  20:	e022                	sd	s0,0(sp)
  22:	0800                	addi	s0,sp,16
  printf("%d %d\n", f(8)+1, 13);
  24:	4635                	li	a2,13
  26:	45b1                	li	a1,12
  28:	00000517          	auipc	a0,0x0
  2c:	7a850513          	addi	a0,a0,1960 # 7d0 <malloc+0x102>
  30:	00000097          	auipc	ra,0x0
  34:	5e6080e7          	jalr	1510(ra) # 616 <printf>
  exit(0);
  38:	4501                	li	a0,0
  3a:	00000097          	auipc	ra,0x0
  3e:	274080e7          	jalr	628(ra) # 2ae <exit>
  1. 哪些寄存器保存函数的参数?例如,在main对printf的调用中,哪个寄存器保存13?

a0-a7存放函数的参数

由第39行,即如下

asm
  24:	4635                	li	a2,13

可见,寄存器a2存放13

  1. main的汇编代码中对函数f的调用在哪里?对g的调用在哪里(提示:编译器可能会将函数内联)

源代码中, main调用函数f, 函数f调用函数g

在生成的汇编中,main函数进行了内联优化处理。

优化为内联(Inline Optimization)是指将函数的调用替换为函数的实际代码,以减少函数调用的开销。

f(8)+1可能直接替换看作为8+3+1来计算

在汇编语言中,内联优化通常是由编译器在生成汇编代码时自动完成的,程序员不需要手动插入汇编代码。

在第40行,即如下

asm
  26:	45b1                	li	a1,12

可见main直接计算出了结果并储存

  1. printf函数位于哪个地址?
asm
  30:	00000097          	auipc	ra,0x0
  34:	5e6080e7          	jalr	1510(ra) # 616 <printf>

printf函数的调用在第44行,

第一行代码 00000097H=00...0 0000 1001 0111B

对比指令格式,可见imm=0,dest=00001,opcode=0010111

对比汇编指令可知,uipc的操作码是0010111,ra寄存器代码是00001

这行代码将0x0左移12位(还是0x0)加到PC(当前为0x30)上并存入ra中,即ra中保存的是0x30

由第44行的跳转可知跳转的位置为

0x30 + 1510 = 0x030 + 0x5e6 = 0x616

  1. 在main中printf的jalr之后的寄存器ra中有什么值?

jalr (jump and link register):jalr rd, offset(rs1)跳转并链接寄存器

jalr指令会将当前PC+4保存在rd中,然后跳转到指定的偏移地址offset(rs1)

跳转到printf函数,且将PC + 4 = 0x34 + 4 = 0x38 存到ra中

  1. 运行以下代码。
c
unsigned int i = 0x00646c72;
printf("H%x Wo%s", 57616, &i);

程序的输出是什么?这是将字节映射到字符的ASCII码表。

输出取决于RISC-V小端存储的事实。如果RISC-V是大端存储,为了得到相同的输出,你会把i设置成什么?是否需要将57616更改为其他值?

十进制57616 = 十六进制0xE110

小端存储将0x00646c72划分72 6c 64 00

再将各部分十六进制转为十进制对应为114 108 100 0

由ASCII码表 对应为r l d

所以输出为 HE110 World

若为大端存储,i改为0x726c6400,不需要改变57616

  1. 在下面的代码中,“y=”之后将打印什么(注:答案不是一个特定的值)?为什么会发生这种情况?
c
printf("x=%d y=%d", 3);

函数需要两个参数, 而只传入了一个参数, y=后面缺失的参数会使用之前a2中保存的数据