CM.PUSH

RISC-V CM.PUSH 指令详解

指令手册C2 (Zcmp CMPP)

将 reg_list 选择的 ra 与 0 至 12 个 s 寄存器保存到栈并调整 sp

指令语法

cm.push {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-XLEN/8 向低地址保存选中寄存器;成功后 sp 减 stack_adj,其他整数寄存器不变。
没有隐含 ret 或 a0 置零;没有直接等价的单条 32 位编码。
访问允许乱序、合并、重试,故障前可能已有部分访存效果;最终段仅在整条指令可提交时提交。
Zcmp栈与函数压缩指令

指令行为说明

CM.PUSH (Zcmp)将{reg_list}中的寄存器(ra及最多12个s寄存器)推入栈帧,并调整sp(减stack_adj)。stack_adj = stack_adj_base + spimm×16。复用c.fsdsp编码,因此与Zcd不兼容。操作可能多次执行(支持故障恢复),sp调整原子提交。属于Zcmp,依赖Zca。

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

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

cm.push {reg_list}, -stack_adj
执行环境

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

sp=0x00001000, rlist=7 -> mem32[0x00000FFC] <- s2/x18=0x00000030; mem32[0x00000FF8] <- s1/x9=0x00000020; mem32[0x00000FF4] <- s0/x8=0x00000010; mem32[0x00000FF0] <- ra/x1=0x00002001; sp <- 0x00000FC0
16 位编码字段
15..13
funct3
101
12..8
fixed
11000
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 字节。
伪代码地址映射:mem32[0x00000FFC] <- s2/x18=0x00000030; mem32[0x00000FF8] <- s1/x9=0x00000020; mem32[0x00000FF4] <- s0/x8=0x00000010; mem32[0x00000FF0] <- ra/x1=0x00002001。实际 存储可按任意顺序、合并或重复;sp' = 0x00001000 - 64 = 0x00000FC0。
mem32[0x00000FFC] <- s2/x18=0x00000030; mem32[0x00000FF8] <- s1/x9=0x00000020; mem32[0x00000FF4] <- s0/x8=0x00000010; mem32[0x00000FF0] <- ra/x1=0x00002001; sp <- 0x00000FC0
尚未提交架构状态。

快速理解与检索要点

把列表寄存器保存到旧 sp 下方,再令 sp 减去 stack_adj。

RV32/RV64 Zcmp,依赖 Zca,与 Zcd 不兼容。16 位 CMPP:bits[15:13]=101、[12:8]=11000、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-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-XLEN/8 向低地址保存选中寄存器;成功后 sp 减 stack_adj,其他整数寄存器不变。
  • 没有隐含 ret 或 a0 置零;没有直接等价的单条 32 位编码。
  • 访问允许乱序、合并、重试,故障前可能已有部分访存效果;最终段仅在整条指令可提交时提交。

容易混淆 / 常见误区

与Zcd(C+D)不兼容,复用c.fsdsp编码
寄存器列表和栈调整受编码限制
需要幂等内存(因故障恢复可能重复执行存储)
寄存器列表不支持{ra, s0-s10}不含s11

常见问题

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

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

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

不是。存储可能已可见;总线不精确故障是在提交前等待还是在指令完成后处理,由平台定义。