cs61c 学习笔记
Performance
- CPU execution time: CPU 执行时间
- CPU clock cycle: CPU 时钟周期数
- clock rate: 时钟频率
- clock cycle time: 时钟周期
CPU execution time = CPU clock cycle * clock cycle time = CPU clock cycle / clock rate
project 2
函数调用
caller 可以随意使用保存寄存器, s0 - s11, 在caller调用callee后,保存寄存器的值不会改变,对于callee来说,使用保存寄存器前,应先保存保存寄存器的值到内存中,即压栈,在callee返回前,应对保存寄存器中的值进行恢复,即弹栈,因此,保存寄存器为callee保存寄存器
一个函数可能是caller,也可能是callee,因此,当函数被一个函数调用后且需要调用其他函数时,当他需要使用保存寄存器时,要先将保存寄存器保存到内存中进行压栈,注意,返回地址也需要保存,到他需要返回时,将保存寄存器中的值和返回地址进行恢复
可以说callee的职责的确保保存寄存器中的值不会改变,所以caller可以随意使用保存寄存器,而对于临时寄存器(t0 - t6)和参数寄存器(a0 - a7),不能保证在函数调用后不会改变,当然,临时寄存器和参数寄存器也可以保存到内存中,并在函数调用结束后进行恢复,因此临时寄存器和参数寄存器为caller保存寄存器
跳转
分支跳转不会将pc + 4,即跳转之后不会返回
risc-v
procedure
x1为返回地址寄存器
1 | jal x1, main # 跳转到main函数,并将下一条指令存储在x1寄存器中 |
1 | jalr x13, 0(x1) # 跳转到x1寄存器的地址,并将下一条指令存储在x13寄存器中 |
1 | jalr x0, 0(x1) # 将控制器还给调用者,跳转到x1中,并将下一条指令存储在x0中,将任何东西存储在x0寄存器中没有任何效果 |
jal 和 jalr的区别
jal指令:
- jal 是”Jump and Link” 的缩写。
- jal 用于在无条件跳转的同时,将下一条指令的地址(返回地址)保存到指定的寄存器中。
- 典型的用法是在函数调用时,使用 jal 将控制权跳转到目标函数,并将返回地址存储在指定寄存器中,以便在函数结束后返回到调用点。
1 | # 调用目标函数 |
jalr指令:
- jalr 是”Jump and Link Register” 的缩写。
- jalr 用于通过寄存器中的地址进行无条件跳转,同时将下一条指令的地址(返回地址)保存到指定的寄存器中。
- 典型的用法是通过加载寄存器中的地址来实现间接函数调用。
1 | # 通过寄存器 t0 中的地址进行跳转,并将返回地址保存在 ra 中 |
risc-v 如何构建32位立即数
U-type指令的立即数有20位,I-type指令的立即数有12位,所以可以通过一条U-type指令和I-type指令来构建
lui(load upper immediate)将所携带的20位立即数载入目标寄存器的高位,并将低12位置零,然后可以使用标准的立即指令来创建32位常量,如 lui 后面是 addi 指令,则可以构建目标寄存器的低12位
- 将lui指令的31到12位加载到寄存器
- 低12位清零
- addi加载低12位
注意:
如果加载的立即数第11位为1,会出现addi加负数的情况
例如:
加载 00010011 11110110 00101111 11111111,即0x13F62FFF
高20位: 00010011 11110110 0010, 即0x13F62
低12位: 1111 11111111,即0xFFF
如果lui不加1:
1 | lui x1, 0x13F62 |
当进行运算后,我们得到0x13F61FFF,显然结果不正确
我们将lui加1后:
1 | lui x1, 0x13F63 |
结果为,0x13F62FFF
auipc(add upper immediate to pc),同U-type一样,会将所携带的20位立即数载入目标寄存器的高20位,并将低12位置零,生成32位数,然后与 pc 相加,并放入目标寄存器
auipc中的20位立即数和jalr中的12位立即数组合,可以执行流程转移到任何32位pc相对地址,而auipc与普通加载后存储指令中的12位立即数偏移量,可以访问任何32位pc相对数据
对于控制流的转移(control-flow transfers)和访问数据,AUIPC 指令支持使用两条指令序列访问基于 PC 的任意偏移量(access arbitrary offsets from the PC)。 AUIPC 和 JALR 里的 12 位立即数的组合,能够转移控制到任意的 32 位 PC 相对地址( AUIPC 先把 32位立即数的高 20 位,补上低 12 位全 0跟 PC 相加,把结果存到某个寄存器里, JALR 再把这个寄存器和 32 位立即数的低 12 位相加,跳转到相加得到的目标地址。也就是实现了 32 位立即数加上 PC的目标地址),而 AUIPC 加上正常的(regular)取数和存数指令里的 12 位偏移量,能访问任意的 32 位 PC 相对数据地址(AUIPC 先把 32 位立即数的高 20 位,补上低 12 位全 0 跟 PC 相加,把结果存到某个寄存器里, 取数和存数指令再把这个寄存器和 32 位立即数的低 12 位相加,相加的结果为访问数据的目标地址。也就是实现了 32 位立即数加上 PC 的目标地址)。
获取当前 PC 的值可以通过(使用 AUIPC 指令并)把 U 型立即数置为全 0。尽管一个 JAL+4 指令也能用来获取当前 PC 值,但这可能会导致在很简单的微体系结构里打断流水线(cause pipeline breaks),或是在更复杂的微体系结构里破坏(pollute)BTB 结构
参考:
整型计算指令 | RISC-V 指令集手册(卷一) (gitbooks.io)
18 RISC-V指令精讲(三):跳转指令实现与调试 (lianglianglee.com)
RISC-V指令集讲解(5)条件和无条件跳转指令 - 知乎 (zhihu.com)
计算机组成原理笔记-算术逻辑单元ALU - 知乎 (zhihu.com)