CM.POP

RISC-V CM.POP 指令详解

指令手册C2 (Zcmp CMPP)

从栈恢复 reg_list 选择的 ra 与 0 至 12 个 s 寄存器并调整 sp

指令语法

cm.pop {reg_list}, stack_adj
操作数说明
RV32/RV64 Zcmp,依赖 Zca,与 Zcd 不兼容;16 位 CMPP 格式,不使用 x8–x15 紧凑寄存器字段。
rlist 选择 ra/x1 及 s0/x8、s1/x9、s2–s11/x18–x27 的连续列表;0–3 保留,RV32E 仅允许 4–6。
rlist=15 同时包含 s10/s11,不能只到 s10。stack_adj 为按列表和 XLEN 向上取整到 16 字节的 base 加 spimm×16(spimm=0–3)。
按伪代码从旧 sp+stack_adj-XLEN/8 向低地址恢复选中寄存器;成功后 sp 加 stack_adj。
没有隐含 ret 或 a0 置零;没有直接等价的单条 32 位编码。
访问允许乱序、合并、重试,故障前可能已有部分访存效果;最终段仅在整条指令可提交时提交。
Zcmp栈与函数压缩指令

指令行为说明

CM.POP (Zcmp)从栈帧弹出{reg_list}中的寄存器并调整sp(加stack_adj)。与CM.PUSH配对使用。注意CM.POP不执行ret,仅恢复寄存器并回收栈帧。复用c.fsdsp编码,与Zcd不兼容。属于Zcmp,依赖Zca。

CM.POP 指令解析与执行动画

仅展示 Zcmp 架构可见序列和条件;访问次序、合并或重试不代表实现细节。

cm.pop {reg_list}, stack_adj
执行环境

Zcmp 依赖 Zca 且与 Zcd 不兼容。正确执行要求 sp 指向幂等内存;RV32E 仅允许 rlist=4-6。输入值为保存前寄存器值或成功加载的内存值;complete 假设 EEI 允许全部访问,不代表任意地址都会成功。

sp=0x00001000, rlist=7 -> s2/x18 <- mem32[0x0000103C]=0x00000030; s1/x9 <- mem32[0x00001038]=0x00000020; s0/x8 <- mem32[0x00001034]=0x00000010; ra/x1 <- mem32[0x00001030]=0x00002001; sp <- 0x00001040
16 位编码字段
15..13
funct3
101
12..8
fixed
11010
7..4
rlist
0111
3..2
spimm
11
1..0
op
10
rlist=7 还原为 {ra/x1, s0/x8, s1/x9, s2/x18};stack_adj_base=16;stack_adj=16+3×16=64 字节。
伪代码地址映射:s2/x18 <- mem32[0x0000103C]=0x00000030; s1/x9 <- mem32[0x00001038]=0x00000020; s0/x8 <- mem32[0x00001034]=0x00000010; ra/x1 <- mem32[0x00001030]=0x00002001。实际 加载可按任意顺序、合并或重复;sp' = 0x00001000 + 64 = 0x00001040。
s2/x18 <- mem32[0x0000103C]=0x00000030; s1/x9 <- mem32[0x00001038]=0x00000020; s0/x8 <- mem32[0x00001034]=0x00000010; ra/x1 <- mem32[0x00001030]=0x00002001; sp <- 0x00001040
尚未提交架构状态。

快速理解与检索要点

从栈加载列表寄存器,再令 sp 加上 stack_adj。

RV32/RV64 Zcmp,依赖 Zca,与 Zcd 不兼容。16 位 CMPP:bits[15:13]=101、[12:8]=11010、rlist=[7:4]、spimm=[3:2]、[1:0]=10。
rlist=4 为 {ra};5–14 依次加上 s0 至 s9;15 为 {ra,s0-s11},没有 {ra,s0-s10}。0–3 保留;RV32E 仅允许 4–6。
stack_adj_base=ceil(寄存器数×XLEN/8÷16)×16;stack_adj=base+spimm×16,spimm=0–3。RV32E 的 base=16。
从旧 sp+stack_adj-XLEN/8 开始,按选中寄存器号降序,每次减 XLEN/8 恢复。没有直接等价的单条 32 位编码。
访问可乱序、合并或重复;正确执行要求幂等内存。精确故障前可能已有部分寄存器加载;最终 sp 仅在整条指令能提交时原子提交。

常见使用场景

函数尾声

RV32I Zcmp、访问成功:{ra,s0-s2} 共 16 字节,stack_adj=64 可编码为 rlist=7、spimm=3。PUSH 将旧 sp 下的值保存后 sp-=64;POP 类从旧 sp+48 至旧 sp+60 恢复后 sp+=64;返回形式使用恢复后的 ra,POPRETZ 另将 a0 置零。

使用前检查清单

语法检查
  • RV32/RV64 Zcmp,依赖 Zca,与 Zcd 不兼容;16 位 CMPP 格式,不使用 x8–x15 紧凑寄存器字段。
  • rlist 选择 ra/x1 及 s0/x8、s1/x9、s2–s11/x18–x27 的连续列表;0–3 保留,RV32E 仅允许 4–6。
语义检查
  • rlist=15 同时包含 s10/s11,不能只到 s10。stack_adj 为按列表和 XLEN 向上取整到 16 字节的 base 加 spimm×16(spimm=0–3)。
  • 按伪代码从旧 sp+stack_adj-XLEN/8 向低地址恢复选中寄存器;成功后 sp 加 stack_adj。
  • 没有隐含 ret 或 a0 置零;没有直接等价的单条 32 位编码。
  • 访问允许乱序、合并、重试,故障前可能已有部分访存效果;最终段仅在整条指令可提交时提交。

容易混淆 / 常见误区

与Zcd不兼容
CM.POP不执行ret,仅恢复寄存器并回收栈帧
寄存器列表和栈调整受编码限制

常见问题

栈调整可以是任意 16 的倍数吗?

不可以。先按寄存器列表与 XLEN 计算 base,只允许 base、base+16、base+32、base+48。

故障前的访存一定没有效果吗?

不是。部分目的寄存器可能已被加载;返回陷阱后会重新执行序列,不能把加载全部当作原子操作。