D965 - The guest's float mode decides a module's float controls

Status: decided Date: 2026-10-25 known_by: published

A program's FLOAT_MODE (PGM_RSRC1 bits 19:12, for compute and for every graphics stage) is translated into SPIR-V float controls: 32-bit "keep" becomes DenormPreserve 32 and "flush" DenormFlushToZero 32. The partial modes - keep on input only, or on output only - cannot be expressed; they are translated by their outputs, and the translation says so. The mode is part of the module's cache key. Every module also declares SignedZeroInfNanPreserve 32. Each mode is declared only where the device offers it; the device's float-controls properties are read once, a module asking for one it does not offer gets the nearest it does, or none, and the log says so once a module. Compute programs read COMPUTE_PGM_RSRC1's own field rather than assuming flush to zero. A device that offers neither mode leaves subnormals to the driver, which may decide per instruction; tests comparing two translations there allow a subnormal-against-zero difference and nothing else. Separately, v_rcp_f32 is computed over every float, scaling a divisor outside 2^±126 by 2^±64, since OpFDiv promises nothing there.

The field's layout is Mesa's (src/amd/registers/gfx10.json for each stage's PGM_RSRC1, and src/amd/compiler/aco_ir.h for the 32-bit and 16/64-bit subnormal bits).

Why: lane-loop fusion (D956) changed which code shape a subtraction of two subnormals lands in, and with no mode declared the driver flushed it in one model and kept it in the other: the result depended on the shape of the code, not on the guest. The guest says what it wants; the module should carry it. Without SignedZeroInfNanPreserve, a driver may return 0 for infinity times a number, and declaring float controls a device does not offer produces modules it rejects.

Rejected: