"Zicond": Integer Conditional Operations

Part III Linux boot: optional Vol. I (Unprivileged) pp. 62–64 · ~3 min read

Unpredictable branches cost pipelines dearly, and secret-dependent branches leak timing. Zicond adds exactly two R-type instructions — conditional zeroing, not a full conditional move — from which branchless selects and conditional arithmetic compose:

00001113125rs22420rs11915101 / 1111412rd117011001160czero.eqz / czero.nez (OP, funct7 0000111)
Click a field for its role.

Both carry syntactic dependencies from both sources to rd (no skipping reads, unlike Zimop), and under Zkt their timing must be independent of the data — the constant-time-crypto guarantee.

The cookbook

Composition patterns (§11.2): neutralize an operand, then combine
SequenceLen
rd = (rc==0) ? rs1+rs2 : rs1czero.nez rd, rs2, rc · add rd, rs1, rd2
rd = (rc!=0) ? rs1+rs2 : rs1czero.eqz rd, rs2, rc · add rd, rs1, rd2
cond-sub / -or / -xorSame shape — zero the second operand under the complementary condition, then sub/or/xor.2
rd = (rc==0) ? rs1&rs2 : rs1and rd, rs1, rs2 · czero.eqz rtmp, rs1, rc · or rd, rd, rtmp — AND has no zero identity, so mask the fallback in instead.3
rd = (rc==0) ? rs1 : rs2 (select)czero.nez rd, rs1, rc · czero.eqz rtmp, rs2, rc · add rd, rd, rtmp — exactly one survives, add merges.3 + 1 temp
Dotted-underlined cells have explanations — click one.

Hardware Designer Notes

The datapath is an existence proof of cheapness: the zero-detect on rs2 already exists for branch comparisons, and the writeback mux already exists for load/ALU selection. Wiring them together under two funct3 codes is an afternoon of RTL.

Minimal Linux-boot hart MUST

  • Implement both as single-cycle ALU ops: rd = (condition-test(rs2)) ? 0 : rs1 — an OR-reduce on rs2 and a 2:1 mux on the writeback path
  • If you claim Zkt: verify the mux path has no value-dependent short-circuit timing

MAY simplify / trap-and-emulate

  • Fuse the documented 2-instruction pairs (czero + add/sub/or/xor) into one internal micro-op — the sequences were designed to be fusion-friendly
  • Skip Zicond on a minimal core (compilers fall back to branches) — but it is in RVA23, and the OR-reduce + mux is nearly free

Check yourself — Zicond

1.Express rd = (rc != 0) ? (rs1 + rs2) : rs1 with Zicond.

2.Why does a full conditional select need THREE instructions (two czeros + add)?

3.What extra guarantee do the czero instructions gain when Zkt is implemented?

3 questions