Skip to content

CS61C Lecture09: RISC-V 位操作与函数调用

约 752 个字 47 行代码 预计阅读时间 4 分钟 共被读过


一、位操作指令详解

1. 位掩码操作

Text Only
andi x5, x6, 0xFF        # 取最低字节(x5 = x6 & 0x000000FF)
andi x7, x8, 0xFF000000  # 取最高字节(x7 = x8 & 0xFF000000)

掩码应用场景
- 提取颜色通道(ARGB格式)
- 网络协议头解析
- 字符编码处理

2. 逻辑运算技巧

操作 实现方式 示例
按位取反 XOR全1掩码 xori x5, x6, 0xFFFFFFFF
清除特定位 AND取反掩码 andi x5, x6, 0xFFFFF0FF(清除第8-11位)
设置特定位 OR置位掩码 ori x5, x6, 0x00000F00

3. 移位指令对比

指令 格式 行为 用途
sll sll rd, rs1, rs2 逻辑左移(补0) 快速乘法(2^n倍)
slli slli rd, rs1, imm 立即数逻辑左移
srl srl rd, rs1, rs2 逻辑右移(补0) 无符号数除法
sra sra rd, rs1, rs2 算术右移(符号扩展) 有符号数除法
srai srai rd, rs1, imm 立即数算术右移

示例

Text Only
slli x5, x6, 3    # x5 = x6 * 8
srai x7, x8, 2    # x7 = x8 / 4(有符号数)

二、程序控制核心机制

1. 程序计数器(PC)

  • 32位寄存器,存储下一条指令的字节地址
  • 默认行为:PC += 4(顺序执行)
  • 跳转时:PC = target_address

2. 寄存器别名表

物理寄存器 符号名称 用途
x0 zero 常数0
x1 ra 返回地址
x2 sp 栈指针
x10-x17 a0-a7 参数/返回值
x5-x7, x28-x31 t0-t6 临时寄存器

三、伪指令解析

伪指令 实际指令 功能
mv rd, rs addi rd, rs, 0 寄存器复制
li rd, imm 组合指令(可能包含lui+addi) 加载立即数
nop addi x0, x0, 0 空操作(用于流水线控制)
j label jal x0, label 无条件跳转
ret jalr x0, 0(x1) 函数返回

nop的三大作用
1. 填充流水线气泡
2. 对齐指令地址
3. 软件延时(不推荐)


四、函数调用机制

1. 函数调用六步曲

  1. 参数传递:a0-a7寄存器(前8个参数)
  2. 控制转移:jal指令(保存返回地址到ra)
  3. 栈帧分配addi sp, sp, -framesize
  4. 局部存储:保存被调用者保存寄存器(s0-s11)
  5. 返回值设置:a0/a1寄存器
  6. 资源释放addi sp, sp, framesize + ret

2. 关键跳转指令对比

指令 格式 行为 用途
j jal x0, offset PC = PC + offset 短距离跳转
jal jal rd, offset rd=PC+4; PC=PC+offset 函数调用
jalr jalr rd, offset(rs1) rd=PC+4; PC=rs1+offset 间接跳转
jr jalr x0, 0(rs1) PC = rs1 寄存器跳转

调用示例

Text Only
# 调用函数func
    addi a0, x0, 5     # 设置参数
    jal ra, func        # 跳转并保存返回地址
    ...                 # 后续代码

func:
    addi sp, sp, -16    # 分配栈空间
    sw ra, 12(sp)       # 保存返回地址
    ...                 # 函数体
    lw ra, 12(sp)      # 恢复返回地址
    addi sp, sp, 16     # 释放栈空间
    ret                 # 返回

五、函数调用规范

1. 寄存器保存规则

寄存器类型 保存责任 寄存器列表
调用者保存 调用者负责保存 t0-t6, a0-a7
被调用者保存 被调用者负责保存 s0-s11, ra

2. 栈帧结构示例

Text Only
High Address
|----------------|
| 保存的s1       | <- sp + 24
|----------------|
| 保存的s0       | <- sp + 20
|----------------|
| 保存的ra       | <- sp + 16
|----------------|
| 局部变量2      | <- sp + 12
|----------------|
| 局部变量1      | <- sp + 8
|----------------|
| 参数溢出区     | <- sp + 4
|----------------|
| 当前栈帧       | <- sp
Low Address

六、高级编程技巧

1. 尾调用优化

Text Only
# 普通递归调用
factorial:
    addi sp, sp, -8
    sw ra, 4(sp)
    ...
    jal factorial       # 产生新栈帧

# 尾递归优化版
factorial_tail:
    ...
    mv a0, t0          # 直接修改参数
    j factorial_tail    # 复用当前栈帧

2. 多精度运算

Text Only
# 64位加法(x5:x4 = x5:x4 + x7:x6)
add x4, x4, x6         # 低32位相加
sltu t0, x4, x6        # 检测进位
add x5, x5, x7         # 高32位相加
add x5, x5, t0         # 加上进位

七、常见错误分析

  1. 栈不平衡

    Text Only
    # 错误示例
    func:
        addi sp, sp, -12
        ... 
        ret  # 忘记恢复sp!
    

    后果:后续函数调用栈损坏
  2. 未保存调用者保存寄存器

    Text Only
    # 错误示例
    caller:
        mv t0, a0
        jal func
        add a0, t0, a0  # t0可能被func修改
    

    正确做法:调用前保存t0到栈中
  3. 错误使用ra寄存器

    Text Only
    # 错误示例
    func:
        jal helper    # 覆盖ra!
        ret           # 返回地址丢失
    

    正确做法:嵌套调用前保存ra到栈中