首页/指令库/CM-POPRETZ
CM.POPRETZ

RISC-V CM.POPRETZ 指令详解

指令手册C2 (Zcmp CMPP)

恢复寄存器、a0=0、回收栈帧并返回

指令语法

cm.popretz {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。
最终段将 a0 置零、调整 sp、执行 ret;ret 使用恢复后的 ra 清零 bit0,不写新链接。
访问允许乱序、合并、重试,故障前可能已有部分访存效果;最终段仅在整条指令可提交时提交。
Zcmp栈与函数压缩指令

指令行为说明

CM.POPRETZ (Zcmp)弹出{reg_list}中的寄存器,将a0置零,调整sp,然后执行ret。用于返回零值的函数尾声。与POPRET的区别是显式将a0清零。复用c.fsdsp编码。属于Zcmp,依赖Zca。

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

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

cm.popretz {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; a0 <- 0; sp <- 0x00001040; PC <- restored ra & ~1 = 0x00002000
16 位编码字段
15..13
funct3
101
12..8
fixed
11100
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;target = 0x00002001 & ~1 = 0x00002000。
s2/x18 <- mem32[0x0000103C]=0x00000030; s1/x9 <- mem32[0x00001038]=0x00000020; s0/x8 <- mem32[0x00001034]=0x00000010; ra/x1 <- mem32[0x00001030]=0x00002001; a0 <- 0; sp <- 0x00001040; PC <- restored ra & ~1 = 0x00002000
尚未提交架构状态。

快速理解与检索要点

从栈加载列表寄存器,再令 sp 加上 stack_adj。随后将 a0 置零,并用恢复后的 ra 返回。

RV32/RV64 Zcmp,依赖 Zca,与 Zcd 不兼容。16 位 CMPP:bits[15:13]=101、[12:8]=11100、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、a0=0 和 ret 仅在整条指令能提交时原子提交。

常见使用场景

函数尾声

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。
  • 最终段将 a0 置零、调整 sp、执行 ret;ret 使用恢复后的 ra 清零 bit0,不写新链接。
  • 访问允许乱序、合并、重试,故障前可能已有部分访存效果;最终段仅在整条指令可提交时提交。

容易混淆 / 常见误区

会覆盖a0为0(破坏任何已计算的返回值)
与Zcd不兼容

常见问题

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

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

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

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