RISC-V学习笔记(2)
2021/7/14 23:11:41
本文主要是介绍RISC-V学习笔记(2),对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
RISC-V学习笔记(2)
作者:夏风喃喃 参考:计算机组成与设计:硬件/软件接口(RISC-V版)
文章目录
- RISC-V学习笔记(2)
- 第2章 计算机的语言
- 2.1 引言
- 2.2 计算机硬件的操作
- 2.3 计算机硬件的操作数(加减指令)
- 2.3.1 存储器操作数(存取指令)
- 2.3.2 常数或立即数操作数(立即数指令)
- 2.4 有符号数与无符号数
- 2.5 计算机中的指令表示(指令格式)
- 2.6 逻辑操作(移位,逻辑运算指令)
- 2.7 用于决策的指令(条件判断分支指令)
- 2.7.1 循环(循环判断分支指令)
- 2.7.2 边界检查的简便方法(其他条件判断分支指令)
- 2.7.3 case/switch语句(条件判断跳转指令)
- 2.8 计算机硬件对过程的支持(函数操作指令)
- 2.8.1 使用更多的寄存器(函数操作寄存器换出指令)
- 2.8.2 嵌套过程(函数操作递归指令)
- 2.9 人机交互(字符串操作指令)
- 2.10 对大立即数的RISC-V编址和寻址
- 2.10.1 大立即数(大立即数加载指令)
- 2.10.2 分支中的寻址
- 2.10.3 RISC-V寻址模式总结
- 2.10.4 机器语言译码
- 2.11 指令与并行性:同步
- 2.12 翻译并启动程序
- 2.12.1 编译器
- 2.12.2 汇编器
- 2.12.3 链接器
- 2.12.4 加载器
- 2.12.5 动态链接库
- 2.13 以C排序程序为例的汇总整理
- 2.13.1 swap过程
- 2.13.2 sort过程
- 2.14 数组与指针
- 2.14.1 用数组实现clear
- 2.14.2 用指针实现clear
- 2.18 实例:RISC-V指令系统的剩余部分
第2章 计算机的语言
2.1 引言
名称 | 寄存器号 | 用途 | 调用时是否保存 |
---|---|---|---|
x0 | 0 | 常数0 | 不适用 |
x1(ra) | 1 | 返回赋值(链接寄存器) | 是 |
x2(sp) | 2 | 栈指针 | 是 |
x3(gp) | 3 | 全局指针 | 是 |
x4(tp) | 4 | 线程指针 | 是 |
x5~x7 | 5~7 | 临时 | 否 |
x8~x9 | 8~9 | 保存 | 是 |
x10~x17 | 10~17 | 参数/结果 | 否 |
x18~x27 | 18~27 | 保存 | 是 |
x28~x31 | 28~31 | 临时 | 否 |
2.2 计算机硬件的操作
RISC-V一个算术指令只执行一个操作,并且必须总是只有三个变量。
类似于加法的操作一般有三个操作数:两个被加到一起的数和一个放置总和的位置。
add a, b, c //The sum of b and c is placed in a
2.3 计算机硬件的操作数(加减指令)
RISC-V算术指令的三个操作数必须从32个64位寄存器(RV64)选择,RISC-V约定寄存器编号为x01-x31。
加法:
add x19, x20, x21 //The sum of x20 and x21 is placed in x19
2.3.1 存储器操作数(存取指令)
内存和寄存器之间传输数据的指令,称为数据传输指令。
取双字:(取内存中的A[8]至寄存器x9,字节寻址8×8偏移地址为64)
ld x9, 64(x22) //x22存放内存中数组的基址,偏移64,取出的数据A[8]存放至寄存器x9
存双字:(将寄存器x9中数据存至内存的A[12],字节寻址8×12偏移地址为96)
sd x9, 96(x22) //x22存放内存中数组的基址,偏移96,寄存器x9的数据存至A[12]
2.3.2 常数或立即数操作数(立即数指令)
RISC-V算术指令有一个常数操作数的快速加指令称为立即数加。
立即数加:
addi x22, x22 4 //x22 = x22 + 4
2.4 有符号数与无符号数
64位无符号数:
(
x
63
×
2
63
)
+
(
x
62
×
2
62
)
+
…
…
+
(
x
1
×
2
1
)
+
(
x
0
×
2
0
)
(x_{63}×2^{63})+(x_{62}×2^{62})+……+(x_{1}×2^{1})+(x_{0}×2^{0})
(x63×263)+(x62×262)+……+(x1×21)+(x0×20)
64位有符号数:
(
x
63
×
−
2
63
)
+
(
x
62
×
2
62
)
+
…
…
+
(
x
1
×
2
1
)
+
(
x
0
×
2
0
)
(x_{63}×-2^{63})+(x_{62}×2^{62})+……+(x_{1}×2^{1})+(x_{0}×2^{0})
(x63×−263)+(x62×262)+……+(x1×21)+(x0×20)
2.5 计算机中的指令表示(指令格式)
RISC-V的指令均为32位。
R(寄存器)型格式指令:(用于三个操作数均在寄存器)
funct7 | rs2 | rs1 | funct3 | rd | opcode |
---|---|---|---|---|---|
7位 | 5位 | 5位 | 3位 | 5位 | 7位 |
opcode、funct3、funct7:操作码,指令的基本操作。 rd:目的操作数寄存器,用来存放操作结果。 rs1:第一个源操作数寄存器。 rs2:第二个源操作数寄存器。
I(立即数)型格式指令:(用于立即数操作或从内存取数据)
immediate | rs1 | funct3 | rd | opcode |
---|---|---|---|---|
12位 | 5位 | 3位 | 5位 | 7位 |
opcode、funct3:操作码,指令的基本操作。 rd:目的操作数寄存器,用来存放操作结果。 rs1:源操作数寄存器或基址寄存器。 immediate:立即数源操作数或偏移量。
S(存放)型格式指令:(用于向内存存数据)
immediate[11:5] | rs2 | rs1 | funct3 | immediate[4:0] | opcode |
---|---|---|---|---|---|
7位 | 5位 | 5位 | 3位 | 5位 | 7位 |
opcode、funct3:操作码,指令的基本操作。 rs1:基址寄存器。 rs2:源操作数寄存器。 immediate:偏移量。
2.6 逻辑操作(移位,逻辑运算指令)
RISC-V的移位操作通常使用 I 型格式指令,因为64位寄存器数据移位不会大于63位,所以immediate的低6位被使用。
funct6 | immediate | rs1 | funct3 | rd | opcode |
---|---|---|---|---|---|
6位 | 6位 | 5位 | 3位 | 5位 | 7位 |
逻辑左移与右移:(移动的空位用0补充)
sll x11, x19, x10 //reg x11 = reg x19 << reg x10 slli x11, x19, 4 //reg x11 = reg x19 << 4 bits srl x11, x19, x10 //reg x11 = reg x19 >> reg x10 srli x11, x19, 4 //reg x11 = reg x19 >> 4 bits
算术右移:(移动的空位用符号扩展补充)
sra x11, x19, x10 //reg x11 = reg x19 >> reg x10 srai x11, x19, 4 //reg x11 = reg x19 >> 4 bits
按位与,按位或:
and x9, x10, x11 //reg x9 = reg x10 & reg x11 andi x9, x10, 4 //reg x9 = reg x10 & 4 or x9, x10, x11 //reg x9 = reg x10 | reg x11 ori x9, x10, 4 //reg x9 = reg x10 | 4
按位异或,按位取反:(取反等价于异或111……111)
xor x9, x10, x12 //reg x9 = reg x10 ^ reg x12 xori x9, x10, 4 //reg x9 = reg x10 ^ 4 xor x9, x10, x11 //reg x9 = ~ reg x10,x11 = 111……111
2.7 用于决策的指令(条件判断分支指令)
分支跳转语句常用于 if 语句。
相等则分支:
beq rs1, rs2, L1 //if rs1 == rs2, branch to label L1
不相等则分支:
bne rs1, rs2, L1 //if rs1 ≠ rs2, branch to label L1
例:( if 条件语句C语言编译为RISC-V代码)
【C】 if (i == j) f = g + h; else f = g - h; f,g,h,i,j分别对应x19-x23这5个寄存器 【RISC-V】 bne x22, x23, Else //go to Else if i ≠ j add x19, x20, x21 //f = g + h (skipped if i ≠ j) beq x0, x0, Exit //if 0 == 0, go to Exit Else: sub x19, x20, x21 //f = g - h (skipped if i = j) Exit:
2.7.1 循环(循环判断分支指令)
循环的RISC-V代码也是通过分支跳转实现。
例:( while 循环语句C语言编译为RISC-V代码)
【C】 while (save[i] == k) i += 1; i,k分别对应x22和x24寄存器,数组的基址保存在x25 【RISC-V】 Loop: slli x10, x22, 3 //Temp reg x10 = i * 8 add x10, x10, x25 //x10 = address of save[i] ld x9, 0(x10) //Temp reg x9 = save[i] bne x9, x24, Exit //go to Exit if save[i] ≠ k addi x22, x22, 1 //i = i + 1 beq x0, x0, Loop //go to Loop Exit:
2.7.2 边界检查的简便方法(其他条件判断分支指令)
小于则分支:
blt rs1, rs2, L1 //if rs1 < rs2, branch to label L1
大于等于则分支:
bge rs1, rs2, L1 //if rs1 ≥ rs2, branch to label L1
无符号小于则分支:
bltu rs1, rs2, L1 //if rs1 < rs2, branch to label L1
无符号大于等于则分支:
bgeu rs1, rs2, L1 //if rs1 ≥ rs2, branch to label L1
2.7.3 case/switch语句(条件判断跳转指令)
case条件语句可以使用分支地址表高效实现,使用跳转-链接指令(jalr)对寄存器中指定的地址执行无条件跳转。
间接跳转:
jalr x0, 0(x1) //unconditionally branch to case address
分支地址表:也称作分支表,一种包含了不同指令序列地址的表。
2.8 计算机硬件对过程的支持(函数操作指令)
过程即函数的实现,RISC-V软件为过程调用分配寄存器时遵循约定:x10-x17八个参数寄存器,用于传递参数或返回值,x1一个返回地址寄存器,用于返回到起始点。
跳转-链接:
jal x1, ProcedureAddress //jump to ProcedureAddress and write return address to x1
无条件跳转:
jal x0, Label //unconditionally branch to Label, discard return address
2.8.1 使用更多的寄存器(函数操作寄存器换出指令)
当函数需要更多的参数时,需要更多的寄存器,将寄存器旧值换出至存储器以满足参数传递,函数调用结束后,需要恢复寄存器旧值,换出寄存器的数据结构是栈(stack),栈按照从高到低的地址顺序(高地址在上,低地址在下)增长,栈指针(stack pointer)是寄存器x2,也称为sp。
例:( 没有调用其他函数的C语言函数编译为RISC-V代码)
【C】 long long int leaf_example(long long int g, long long int h, long long int i, long long int j) { long long int f; f = (g + h) - (i + j); return f; } g,h,i,j分别对应x10-x13寄存器,f对应于x20 【RISC-V】 leaf_example: addi sp, sp, -24 //adjust stack to make room for 3 times sd x5, 16(sp) //save reg x5 for use afterwards sd x6, 8(sp) //save reg x6 for use afterwards sd x20, 0(sp) //save reg x20 for use afterwards add x5, x10, x11 //reg x5 contains g + h add x6, x12, x13 //reg x6 contains i + j sub x20, x5, x6 //f = x5 - x6, which is (g + h) - (i + j) addi x10, x20, 0 //returns f (x10 = x20 + 0) ld x20, 0(sp) //restore reg x20 for caller ld x6, 8(sp) //restore reg x6 for caller ld x5, 16(sp) //restore reg x5 for caller addi sp, sp, 24 //adjust stack to delete 3 times jalr x0, 0(x1) //branch back to calling routine
RISC-V软件将19个寄存器分为两组:x5-x7以及x28-x31是临时寄存器,在过程调用中不被被调用者保存;x8-x9以及x18-x27是保存寄存器,在过程调用中必须被保存。
2.8.2 嵌套过程(函数操作递归指令)
例:( 计算阶乘的C语言递归函数编译为RISC-V代码)
【C】 long long int fact (long long int n) { if (n < 1) return (1); else return (n * fact (n - 1)) } n对应x10参数寄存器 【RISC-V】 fact: addi sp, sp, -16 //adjust stack to make room for 2 times sd x1, 8(sp) //save the return address sd x10, 0(sp) //save the argument n addi x5, x10, -1 //x5 = n - 1 bge x5, x0, L1 //if (n - 1) >= 0, go to L1 addi x10, x0, 1 //return 1 addi sp, sp, 16 //pop 2 items off stack jalr x0, 0(x1) //return to caller L1: addi x10, x10, -1 //n >= 1: argument gets (n - 1) jal x1, fact //call fact with (n - 1) addi x6, x10, 0 //return from jal: move result of fact (n - 1) to x6 ld x10, 0(sp) //restore argument n ld x1, 8(sp) //restore the return address addi sp, sp, 16 //adjust stack pointer to pop 2 items mul x10, x10, x6 //return n * fact (n - 1) jalr x0, 0(x1) //return to the caller
为了简化C语言中的静态全局变量的访问,RISC-V编译器保留了一个寄存器x3用作全局指针或gp。
例:( 计算累加的C语言递归函数编译为RISC-V代码)
【C】 long long int sum (long long int n, long long int acc) { if (n > 0) return sum (n - 1, acc + n); else return acc } n,acc对应x10,x11寄存器, 结果放入x12 【RISC-V】 sum: ble x10, x0, sum_exit //go to sum_exit if n <= 0 add x11, x11, x10 //add n to acc addi x10, x10, -1 //subtract 1 from n jal x0, sum //jump to sum sum_exit: addi x12, x11, 0 //return value acc jalr x0, 0(x1) //return to caller
2.9 人机交互(字符串操作指令)
例:( 字符串复制的C语言编译为RISC-V代码)
【C】 void strcpy (char x[], char y[]) { size_t i; i = 0; while ((x[i] = y[i] != '\0') /*copy & test byte*/ ) i += 1; } x,y基址对应x10,x11寄存器, i对应x19寄存器 【RISC-V】 strcpy: addi sp, sp, -8 //adjust stack for 1 more item sd x19, 0(sp) //save x19 add x19, x0, x0 //i = 0 + 0 L1: add x5, x19, x11 //address of x[i] in x5 lbu x6, 0(x5) //x[6] = y[i] add x7, x19, x10 //address of x[i] in x7 sb x6, 0(x7) //x[i] = y[i] beq x6, x0, L2 addi x19, x19, 1 //i = i + 1 jal x0, L1 //go to L1 L2: ld x19, 0(sp) //restore old x19 addi sp, sp, 8 //pop 1 doubleword off stack jalr x0, 0(x1) //return
加载无符号字节:
lbu x12, 0(x10) //Read byte from source
加载无符号字:
lwu x12, 0(x10) //Read byte from source
2.10 对大立即数的RISC-V编址和寻址
2.10.1 大立即数(大立即数加载指令)
将64位常量加载到寄存器,需要使用lui
(load upper immediate)指令,用于将20位常数加载到寄存器的第31位到第12位。将31位的值复制填充到最左边32位,最右边的12位用0填充。lui
使用U型格式指令。
例:( 64位常量加载到寄存器x19编译为RISC-V代码)
00000000 00000000 00000000 00000000 00000000 00111101 00000101 00000000 【RISC-V】 lui x19, 976 //976(decimal) = 0000 0000 0011 1101 0000 addi x19, x19, 1280 //1280(decimal) = 00000101 00000000
2.10.2 分支中的寻址
RISC-V分支指令格式为SB型。
SB(分支)型格式指令:(如bne x10, x11, 2000
)
imm[12] | imm[10:5] | rs2 | rs1 | funct3 | imm[4:1] | imm[11] | opcode |
---|---|---|---|---|---|---|---|
1位 | 6位 | 5位 | 5位 | 3位 | 4位 | 1位 | 7位 |
无条件跳转-链接指令(jal)是唯一使用UJ型格式的指令。
UJ(无条件跳转)型格式指令:(如jal x0, 2000
)
imm[20] | imm[10:1] | imm[11] | imm[19:12] | rd | opcode |
---|---|---|---|---|---|
1位 | 10位 | 1位 | 8位 | 5位 | 7位 |
分支中的寻址使用PC相对寻址,它的地址是PC和指令中的常量之和。
2.10.3 RISC-V寻址模式总结
RISC-V有4种寻址模式:
- 立即数寻址:操作数是指令本身的常量。
- 寄存器寻址:操作数在寄存器中。
- 基址寻址:操作数于内存中,其地址是寄存器和指令中的常量之和。
- PC相对寻址:分支地址是PC和指令中常量之和。
2.10.4 机器语言译码
从机器语言到汇编语言的译码需要编码表和指令格式表来查找。
RISC-V指令的编码:
格式 | 指令 | 操作码 | funct3 | funct6/7 |
---|---|---|---|---|
R | add | 0110011 | 000 | 0000000 |
R | sub | 0110011 | 000 | 0100000 |
R | sll | 0110011 | 001 | 0000000 |
R | xor | 0110011 | 100 | 0000000 |
R | srl | 0110011 | 101 | 0000000 |
R | sra | 0110011 | 101 | 0000000 |
R | or | 0110011 | 110 | 0000000 |
R | and | 0110011 | 111 | 0000000 |
R | lr.d | 0110011 | 011 | 0001000 |
R | sc.d | 0110011 | 011 | 0001100 |
I | lb | 0000011 | 000 | n.a. |
I | lh | 0000011 | 001 | n.a. |
I | lw | 0000011 | 010 | n.a. |
I | ld | 0000011 | 011 | n.a. |
I | lbu | 0000011 | 100 | n.a. |
I | lhu | 0000011 | 101 | n.a. |
I | lwu | 0000011 | 110 | n.a. |
I | addi | 0010011 | 000 | n.a. |
I | slli | 0010011 | 001 | 000000 |
I | xori | 0010011 | 100 | n.a. |
I | srli | 0010011 | 101 | 000000 |
I | srai | 0010011 | 101 | 010000 |
I | ori | 0010011 | 110 | n.a. |
I | andi | 0010011 | 111 | n.a. |
I | jalr | 1100111 | 000 | n.a. |
S | sb | 0100011 | 000 | n.a. |
S | sh | 0100011 | 001 | n.a. |
S | sw | 0100011 | 010 | n.a. |
S | sd | 0100011 | 111 | n.a. |
SB | beq | 1100111 | 000 | n.a. |
SB | bne | 1100111 | 001 | n.a. |
SB | blt | 1100111 | 100 | n.a. |
SB | bge | 1100111 | 101 | n.a. |
SB | bltu | 1100111 | 110 | n.a. |
SB | bgeu | 1100111 | 111 | n.a. |
U | lui | 0110111 | n.a. | n.a. |
UJ | 1101111 | n.a. | n.a. |
RISC-V指令格式:
名称 (字段大小) | 字段 | 备注 | |||||
7位 | 5位 | 5位 | 3位 | 5位 | 7位 | ||
R型 | funct7 | rs2 | rs1 | funct3 | rd | opcode | 算术指令格式 |
I型 | immediate[11:0] | rs1 | funct3 | rd | opcode | 加载&立即数算术 | |
S型 | immediate[11:5] | rs2 | rs1 | funct3 | immediate[4:0] | opcode | 存储 |
SB型 | immediate[12,10:5] | rs2 | rs1 | funct3 | immediate[4:1,11] | opcode | 条件分支格式 |
UJ型 | immediate[20,10:1,11,19:12] | rd | opcode | 无条件跳转 | |||
U型 | immediate[31:12] | rd | opcode | 大立即数格式 |
2.11 指令与并行性:同步
当来自两个线程的访存请求出现数据竞争时,通常需要以原子交换原语形式构建基本同步原语。它是构建同步机制的典型操作,它将寄存器中的值与存储器中的值进行交换。
假设构建一个简单的锁变量,其中值0表示锁变量可用,值1表示锁变量被占用。处理器尝试通过将寄存器中的1与该锁变量对应的内存地址中的值进行交换来加锁。如果其他处理器已访问该锁变量,则交换指令返回值1,表明该锁已被占用,否则为0,表示加锁成功,且锁变量被交换为1,以防止其他处理器也加锁成功。
在RISC-V中,使用指令对保留加载(load-reserved)双字(lr.d
)和条件存储(store-conditional)双字(sc.d
)实现原子交换。
例:(在寄存器x20中指定的内存位置上实现原子交换编译为RISC-V代码)
【RISC-V】 addi x12, x0, 1 //copy locked value again: lr.d x10, (x20) //load-reserved to read lock bne x10, x0, again //check if it is 0 yet sc.d x11, x12, (x20) //attempt to store new value bne x11, x0, again //branch if store fails sd x0, 0(x20) //free lock by writting 0
2.12 翻译并启动程序
C程序转换为计算机上可运行程序需要四个步骤:
文件名后缀:
文件 | UNIX | MS-DOS |
---|---|---|
C源文件 | x.c | .C |
汇编文件 | x.s | .ASM |
目标文件 | x.o | .OBJ |
静态链接库 | x.a | .LIB |
动态链接库 | x.so | .DLL |
可执行文件 | a.out | .EXE |
2.12.1 编译器
编译器将C程序转换为机器能理解的符号形式——汇编语言程序(assembly language program)。
2.12.2 汇编器
汇编器将汇编语言转换为目标文件,该目标文件是机器指令、数据和将指令正确放入内存所需信息的组合。
汇编器还可以处理机器指令的常见变体,这类指令称为伪指令。如li x9, 123 //load immediate value 123 into register x9
汇编器将其转化为addi x9, x0, 123
;mv x10, x11 //reg x10 gets reg x11
汇编器将其转化为addi x9, x10, 0
;and x9, x10, 15
汇编器将其转化为andi x9, x10, 15
。
2.12.3 链接器
链接器将所有独立汇编的机器语言程序“缝合”在一起。由于独立编译和汇编每个过程,因此更改一行代码只需要编译和汇编一个过程,链接器有用的原因是修正代码要比重新编译和重新汇编快得多。
2.12.4 加载器
将可执行程序放在主存中以准备执行的系统程序。
2.12.5 动态链接库
动态链接库(DLL)是在执行期间链接到程序的库例程。DLL需要额外的空间来存储动态链接所需的信息,但不要求复制或链接整个库。它们在第一次调用历程时会付出大量的开销,但此后只需一个间接跳转。
2.13 以C排序程序为例的汇总整理
2.13.1 swap过程
例:( swap过程的C语言编译为RISC-V代码)
【C】 void swap (long long int v[], size_t k) { long long int temp; temp = v[k]; v[k] = v[k+1]; v[k+1] = temp; } v,k对应x10,x11寄存器 【RISC-V】 swap: slli x6, x11, 3 //reg x6 = k * 8 add x6, x10, x6 //reg x6 = v + (k * 8) ld x5, 0(x6) //reg x5 (temp) = v[k] ld x7, 8(x6) //reg x7 = v[k + 1] sd x7, 0(x6) //v[k] = reg x7 sd x5, 8(x6) //v[k + 1] = reg x5 (temp) jalr x0, 0(x1) //return to calling routine
2.13.2 sort过程
例:( 冒泡sort过程的C语言编译为RISC-V代码)
【C】 void sort (long long int v[], size_t int k) { size_t i,j; for (i = 0; i < n; i += 1){ for (j = 1 - 1; j >= 0 && v[j] > v[j + 1]; j -= 1){ swap(v,j); } } } v,n对应x10,x11寄存器,i,j对应x19,x20寄存器 【RISC-V】 *******************保存寄存器************************ sort: addi sp, sp, -40 //make room on stack for 5 registers sd x1, 32(sp) //save return address on stack sd x22, 24(sp) //save x22 on stack sd x21, 16(sp) //save x21 on stack sd x20, 8(sp) //save x20 on stack sd x19, 0(sp) //save x19 on stack *********************过程体************************** //移动参数 mv x21, x10 //copy parameter x10 into x21 mv x22, x11 //copy parameter x10 into x22 //外循环 li x19, 0 //i = 0 for1tst: bge x19, x22, exit1 //go to exit1 if i >= n //内循环 addi x20, x19, -1 //j = i - 1 for2tst: blt x20, x0, exit2 //go to exit2 if j < 0 slli x5, x20, 3 //x5 = j * 8 add x5, x21, 3 //x5 = v + (j * 8) ld x6, 0(x5) //x6 = v[j] ld x7, 8(x5) //x7 = v[j + 1] ble x6, x7, exit2 //go to exit2 if x6 < x7 //参数传递和调用 mv x10, x21 //first swap parameter is v mv x11, x20 //second swap parameter is j jal x1, swap //call swap //内循环 addi x20, x20, -1 //j = j - 1 j for2tst //go to for2tst //外循环 exit2: addi x19, x19, 1 //i += 1 j for1tst //go to for1tst *******************恢复寄存器******************** exit1: ld x19, 0(sp) //restore x19 from stack ld x20, 8(sp) //restore x20 from stack ld x21, 16(sp) //restore x21 from stack ld x22, 24(sp) //restore x22 from stack ld x1, 32(sp) //restore return address from stack addi sp, sp, 40 //restore stack pointer ********************过程返回********************* jalr x0, 0(x1) //return to calling routine
2.14 数组与指针
例:( 清除内存中一个双字序列的C语言编译为RISC-V代码)
【C】 //数组实现 clear1 (long long int array[], size_t int size) { size_t i; for (i = 0; i < size; i += 1) array[i] = 0; } //指针实现 clear2 (long long int *array, size_t int size) { long long int *p; for (p = &array[0]; p < &array[size]; p = p + 1) *p = 0; }
2.14.1 用数组实现clear
【RISC-V】 li x5, 0 //i = 0 loop1: slli x6, x5, 3 //x6 = i * 8 add x7, x10, x6 //x7 = address of array[i] sd x0, 0(x7) //array[i] = 0 addi x5, x5, 1 //i = i + 1 blt x5, x11, loop1 //if (i < size) go to loop1
2.14.2 用指针实现clear
【RISC-V】 mv x5, x10 //p = address of array[0] slli x6, x11, 3 //x6 = size * 8 add x7, x10, x6 //x7 = address of array[size] loop2: sd x0, 0(x5) //memory[p] = 0 addi x5, x5, 8 //p = p + 8 bltu x5, x7, loop2 //if (p < &array[size]) go to loop2
2.18 实例:RISC-V指令系统的剩余部分
RISC-V指令系统体系结构分为基本ISA(称作I)以及五个标准扩展:M、A、F、D、C。
助记符 | 描述 | 指令数 |
---|---|---|
I | 基本体系结构 | 51 |
M | 整数乘法/除法 | 13 |
A | 原子操作 | 22 |
F | 单精度浮点 | 30 |
D | 双精度浮点 | 32 |
C | 压缩指令 | 36 |
RISC-V的基本体系结构及其扩展共有184条指令及13条系统指令。
这篇关于RISC-V学习笔记(2)的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-23增量更新怎么做?-icode9专业技术文章分享
- 2024-11-23压缩包加密方案有哪些?-icode9专业技术文章分享
- 2024-11-23用shell怎么写一个开机时自动同步远程仓库的代码?-icode9专业技术文章分享
- 2024-11-23webman可以同步自己的仓库吗?-icode9专业技术文章分享
- 2024-11-23在 Webman 中怎么判断是否有某命令进程正在运行?-icode9专业技术文章分享
- 2024-11-23如何重置new Swiper?-icode9专业技术文章分享
- 2024-11-23oss直传有什么好处?-icode9专业技术文章分享
- 2024-11-23如何将oss直传封装成一个组件在其他页面调用时都可以使用?-icode9专业技术文章分享
- 2024-11-23怎么使用laravel 11在代码里获取路由列表?-icode9专业技术文章分享
- 2024-11-22怎么实现ansible playbook 备份代码中命名包含时间戳功能?-icode9专业技术文章分享