22.1-7"D": Double-Precision Floating-Point & NaN-Boxing

Part I Linux boot: required Vol. I (Unprivileged) pp. 121–124 · ~5 min read

  • nan-boxing

D depends on F and widens the register file to FLEN = 64. Its instructions are the F set at fmt = 01, defined “analogously” — so the substantive new architecture in this chapter is really one idea, and it’s one your FPU’s every input and output port must implement:

22.2 NaN-boxing

With multiple precisions in one register file, a valid narrower n-bit value is stored in the low n bits with all upper FLEN−n bits set to 1 NaN-boxing . Viewed at any wider width, a boxed value is a negative quiet NaN.

valid box: FLW result63:32 = 0xFFFF_FFFF31:0 = single-precision valueinvalid box fed to FADD.S63:32 ≠ all 1slow bits ignored→ operand becomes0x7fc00000 (canonical NaN)

The box check: computational narrower ops verify the upper bits; failures don’t trap — they poison the operand with canonical NaN.

The rules split cleanly by instruction class:

NaN-boxing behavior by operation class
Writing a narrower value INTO fReading a narrower value OUT
Transfers: FLn / FSn, FMV.n.X / FMV.X.nBox it: upper bits ← all 1sTake low n bits, ignore uppers — NO check
Everything else (arithmetic, converts, compares, FCLASS at width n)Results are boxed like transfersCHECK the box; invalid → operand = n-bit canonical NaN
Dotted-underlined cells have explanations — click one.

22.3–22.7 The D instruction set: F at fmt=01, plus the deltas

FLD/FSD move 64 bits verbatim (NaN payloads preserved) — atomic only if naturally aligned and XLEN ≥ 64 (on RV32, an aligned FLD may legally be two memory operations — recall the RVWMO exception list). Arithmetic, FMA, sign-injection, compares, and FCLASS.D are exact doubles of their F counterparts. The items that aren’t pure copies:

D-specific deltas vs F
Delta
FCVT.S.D / FCVT.D.Srs2 field = source type, fmt = destination type; S.D rounds, D.S is always exact
FCVT.D.W[U]Always exact (every 32-bit integer is representable in double) — rm ignored in effect
FCVT.int.D edge casesIdentical to Table 27 of ch. I·21 — NaN → max positive, NV set
FMV.X.D / FMV.D.XXLEN ≥ 64 only; bit-preserving
Dotted-underlined cells have explanations — click one.
FCVT3127fmt=002625rs2=012420rs11915rm1412rd117OP-FP60FCVT.S.D
Click a field for its role.

Hardware Designer Notes

The NaN-box check/substitute/fill logic is THE new hardware in D — a few gates per operand port, but with outsized verification weight: directed tests must cover (bad box → canonical NaN) on every narrower computational op, and (bad box → raw bits) on every transfer. The RV32 FLD-splits-in-two subtlety matters if you ever build RV32+D: torn doubles across harts are architecturally possible there and reference models know it.

Minimal Linux-boot hart MUST

  • Box every narrower result (upper bits all 1s) — writeback is always full FLEN width
  • Check boxes on computational narrower operands and substitute canonical NaN on failure — silently
  • Skip the check on transfers: FSW/FMV.X.W export raw low bits
  • Guarantee FLD/FSD atomicity only when aligned and XLEN≥64 — on RV32 they may split
  • Keep FCVT.D.S / FCVT.D.W[U] exact — no rounding, no NX

MAY simplify / trap-and-emulate

  • Recode internally (tag NaNs via exponent encoding) or run non-recoded with comparator+fill — both anticipated by the spec
  • Share the FCVT float-to-float path with the later Zfh/Q widths — the rs2-as-type-tag pattern scales

Check yourself — D & NaN-boxing

1.FADD.S executes on an f-register whose upper 32 bits are NOT all 1s (an invalid box). What does the FPU use as that operand?

2.FSW stores from that same badly-boxed register. What goes to memory?

3.What must FLW write into bits 63:32 of the destination f-register (FLEN=64)?

4.On RV32 with D, how does software move the upper 32 bits of an f-register into integer registers?

4 questions