Zfa (depends on F) is the floating-point odds-and-ends drawer — five families that each erase a chronic multi-instruction idiom or a semantic mismatch with IEEE 754-2019:
| Encoding trick | What it fixes | |
|---|---|---|
| FLI.fmt — load constant | FMV.W.X with rs2=1; the rs1 FIELD indexes Table 35’s 32 constants | One instruction instead of a constant-pool load for −1.0, ±min-normal, the 2^n ladder, 0.25…4 in eighths, 8…2^16, +∞, canonical NaN. Chosen from libm usage; never sets flags. |
| FMINM / FMAXM | FMIN/FMAX with bit 13 = 1 | IEEE 2019 minimum/maximum: ANY NaN input → canonical NaN (base FMIN returns the non-NaN operand — the 2008 minNum semantics). |
| FROUND / FROUNDNX | FCVT-shaped with rs2 = 4 / 5 | Round to integral-valued FLOAT per rm. FROUNDNX adds the inexact flag — IEEE’s roundToIntegralExact; both: sNaN → invalid only, zero/∞ pass through. Kills the convert-to-int-and-back dance (with its overflow hazard). |
| FCVTMOD.W.D | FCVT.W.D with rs2=8, rm hardwired RTZ | JavaScript ToInt32: truncate, wrap modulo 2^32 (bits 31:0 of the unbounded result, sign-extended); ±∞/NaN → 0. |
| FMVH.X.D + FMVP.D.X (RV32+D); FMVH.X.Q + FMVP.Q.X (RV64+Q) | OP-FP funct7 1110001/1011001 (D), 1110011/1011011 (Q) | Register-PAIR moves for the too-wide formats — closes the move-through-memory gap left by D-on-RV32 and Q-on-RV64. |
| FLEQ / FLTQ | FLE/FLT with bit 14 = 1 | Quiet comparisons: qNaN inputs don’t raise invalid — C’s isless()/islessequal() in one instruction. (No vector versions planned; masking emulates.) |
The FLI table’s per-format quirks are worth knowing: entry 1 (“minimum positive normal”) is format-relative; for FLI.H, entries 2 and 3 (2⁻¹⁶, 2⁻¹⁵) are subnormal — numerically below entry 1 — and entry 29 (2¹⁶) overflows half-precision entirely, degenerating to a duplicate +∞. Entries 8–22 follow a regular two-mantissa-bit pattern, so the decoder ROM is tiny.
Hardware Designer Notes
Everything here is decode-and-datapath-tweaks on machinery F/D already built; the largest new block is FCVTMOD’s wrap path. If you’re chasing web-workload performance, that one instruction pays for the whole extension.
Minimal Linux-boot hart MUST
- FLI as a 32-entry × 10-bit ROM (sign+exponent+2 mantissa bits) expanded per fmt — no flags, no FPU pipeline needed
- FROUND in the existing rounder with the exponent-bounded shortcut (inputs ≥ 2^significand-bits are already integral)
- FCVTMOD needs the unbounded-width integer path: a 64-bit shifter tail beyond FCVT.W.D’s saturating result
MAY simplify / trap-and-emulate
- Skip Zfa unless targeting RVA23 (which mandates it) — but note FCVTMOD alone is a JavaScript-engine benchmark item
- Implement the RV32 pair moves as two 32-bit read ports on the f-register file’s halves
Check yourself — Zfa
1.How do FMINM.S and FLEQ.S differ from their base-F counterparts?
2.What is FCVTMOD.W.D for, and what does it do with 2^40 + 7?
3.Why does FLI.H's entry 29 load +infinity instead of 2^16?
4.On RV32 with D, how does a double get from f-register to integer registers under Zfa?