有了基本档案架构后,开始动工指令的部分。RISC-V将指令分成数个子集,其中包括RV32I、RV32E、RV64I、RV128I四套整数指令集,以及约14套扩充指令集,虽然号称"精简",最基本的RV32I指令集也有47条指令,实作的effort也是不小。为了能有的放矢的进行实作,目前打算先以实际编译出来最常用的指令开始处理。
为了观察实际上compiler常用的指令,需要先取得RISC-V的GNU toolchain,由于目前只打算支援riscv32架构,因此使用的是riscv32-elf-ubuntu-20.04-nightly-2021.06.26-nightly.tar.gz这个版本的toolchain。解压缩后在riscv/bin资料夹下就可以找到gcc、objdump等常用工具。首先撰写一个最基本的C程式:
int main() { return 0;}
使用gcc编译,并使用odjbump得到完整的assembly:
riscv/bin/riscv32-unknown-elf-gcc -o main.elf main.criscv/bin/riscv32-unknown-elf-objdump -D -M no-aliases main.elf > main.asm
观察入口点_start
的assembly:
00010084 <_start>: 10084: 00002197 auipc gp,0x2 10088: b5c18193 addi gp,gp,-1188 # 11be0 <__global_pointer$> 1008c: c3418513 addi a0,gp,-972 # 11814 <completed.1> 10090: c5018613 addi a2,gp,-944 # 11830 <__BSS_END__> 10094: 8e09 c.sub a2,a0 10096: 4581 c.li a1,0 10098: 2209 c.jal 1019a <memset> 1009a: 00000517 auipc a0,0x0 1009e: 26650513 addi a0,a0,614 # 10300 <atexit> 100a2: c511 c.beqz a0,100ae <_start+0x2a> 100a4: 00000517 auipc a0,0x0 100a8: 26650513 addi a0,a0,614 # 1030a <__libc_fini_array> 100ac: 2c91 c.jal 10300 <atexit> 100ae: 2049 c.jal 10130 <__libc_init_array> 100b0: 4502 c.lwsp a0,0(sp) 100b2: 004c c.addi4spn a1,sp,4 100b4: 4601 c.li a2,0 100b6: 2881 c.jal 10106 <main> 100b8: a8b9 c.j 10116 <exit>
可以发现除了一般的RV32I外,其中为了降低code size大量使用了c.
开头的压缩指令标準扩充(Standard Extension for Compressed Instructions),因此也必须实作扩充指令集C。
至此已经有一个明显的目标,就是将此ELF档能顺利跑完,并且能正确的更新所有的整数register以及PC。为达成此目标,指令实作的顺序就以从_start
开始执行的指令为準。