The last three Zcmp instructions: the plain returning epilogue, and two atomic register-pair moves that grease the argument-shuffling around calls.
cm.popret
cm.popret {reg_list}, stack_adj = cm.popretz without
the li a0,0 — reload, release, ret, with the same body/tail split
(encoding bits 12:8 = 11110). Everything from the popretz page applies;
a0 carries whatever the function computed.
The move pairs (CMMV format)
| Moves | Rules | |
|---|---|---|
| cm.mva01s r1s′, r2s′ | a0 ← r1s′; a1 ← r2s′ | r1s′ may equal r2s′ (same source twice is fine) |
| cm.mvsa01 r1s′, r2s′ | r1s′ ← a0; r2s′ ← a1 | r1s′ ≠ r2s′ required |
Both execute atomically — no observable state has only one of the two moves done. The s-numbering trick: the 3-bit field encodes ABI s-numbers, and hardware maps them across the s-register split (x8–x9, x18–x23) with a two-term formula — cheaper than spending 5-bit x-specifiers.
Hardware Designer Notes
With this page the Zcmp set is complete. The family pattern: everything lives in c.fsdsp’s encoding space (the Zcd trade), everything is microsequenced with an atomic tail, and everything exists to shrink function prologues/epilogues on embedded cores. Next and last in Zc*: table jumps.
Minimal Linux-boot hart MUST
- Implement the s→x mapping exactly: sN = x8+N for N<2, x16+N for N≥2
- Reject (reserve) cm.mvsa01 with r1s′ = r2s′
- Make each pair move atomic — dual write ports or sequenced-but-uninterruptible
MAY simplify / trap-and-emulate
- Crack the pair into two µops internally if no trap/interrupt boundary can fall between them
Check yourself — popret & the mv pairs
1.cm.mvsa01 s3, s3 — legal?
2.Why do the mv-pair instructions use 3-bit s-register specifiers (s0-s7) with a weird xreg mapping formula?
3.What are cm.mva01s/cm.mvsa01 for?