Target: Slang, source/compiler-core/slang-nvrtc-compiler.cpp (the empty
case FloatingPointMode::Precise: at ~:1209).
Summary
-fp-mode precise is documented as "Disable optimization that could change the
output of floating-point computations". FMA contraction changes results — it keeps
the intermediate product at higher precision, so a*b+c does not round the same
way as a separate multiply and add. NVRTC contracts by default (--fmad=true),
and Slang's Precise case for NVRTC is empty, so nothing suppresses it.
This is the same defect, on a different target, that #11933 reported and #11935
fixed for SPIR-V by emitting NoContraction.
Reproducer
fma.slang:
RWStructuredBuffer<float> gOut;
[shader("compute")][numthreads(1,1,1)]
void computeMain(uint3 tid : SV_DispatchThreadID)
{
float a = gOut[0], b = gOut[1], c = gOut[2];
gOut[3] = a * b + c;
}
slangc fma.slang -target ptx -entry computeMain -stage compute -o default.ptx
slangc fma.slang -target ptx -entry computeMain -stage compute -fp-mode precise -o precise.ptx
Evidence
| invocation |
emitted PTX for a * b + c |
| default |
fma.rn.f32 %f4, %f1, %f2, %f3; |
-fp-mode precise |
fma.rn.f32 %f4, %f1, %f2, %f3; — byte-identical to default |
-fp-mode precise -Xnvrtc --fmad=false |
mul.rn.f32 + add.rn.f32 |
cmp reports the default and precise PTX files as identical, so precise
currently has no effect whatsoever on this path.
Suggested fix
Pass --fmad=false from the Precise case in slang-nvrtc-compiler.cpp:
case FloatingPointMode::Precise:
{
cmdLine.addArg("--fmad=false");
break;
}
--fmad=false alone is the complete fix — verified. NVRTC's other
precision-relevant defaults are already at their precise settings
(--prec-div=true, --prec-sqrt=true, --ftz=false), so passing all four
produces byte-identical PTX to passing only --fmad=false (checked on a shader
exercising multiply-add, divide, sqrt and transcendentals). No other flag needs to
be added.
Why this is the right layer
The contraction decision belongs to the downstream compiler, and Slang already owns
the translation from -fp-mode to downstream flags for every other backend
(-Gis for DXC, D3DCOMPILE_IEEE_STRICTNESS for FXC, /fp:precise for MSVC).
The NVRTC case is simply unimplemented rather than deliberately empty; there is no
IR- or emit-level work required, and no Slang-side representation question.
Limits of what was measured
- PTX inspection only. No runtime numerics were compared, so the magnitude of the
accuracy difference contraction causes on real shaders is not quantified here —
only that the emitted instruction changes.
- Performance impact of
--fmad=false was not measured. Disabling contraction will
cost some throughput; that is the expected and intended price of precise, but a
reviewer may want a number before enabling it by default.
- Only the CUDA/NVRTC path was checked. Metal, WGSL and GLSL emit identical source
under both modes, so whatever their downstream compilers do about contraction is
likewise uncontrolled by -fp-mode — worth a separate look, not covered here.
- The reproducer is the six-line
fma.slang quoted above.
Related
Found while establishing what -fp-mode actually switches on each target; the
verification that --fmad=false alone suffices was done by comparing PTX with all
four precision flags against PTX with only that one.
Target: Slang,
source/compiler-core/slang-nvrtc-compiler.cpp(the emptycase FloatingPointMode::Precise:at ~:1209).Summary
-fp-mode preciseis documented as "Disable optimization that could change theoutput of floating-point computations". FMA contraction changes results — it keeps
the intermediate product at higher precision, so
a*b+cdoes not round the sameway as a separate multiply and add. NVRTC contracts by default (
--fmad=true),and Slang's
Precisecase for NVRTC is empty, so nothing suppresses it.This is the same defect, on a different target, that #11933 reported and #11935
fixed for SPIR-V by emitting
NoContraction.Reproducer
fma.slang:Evidence
a * b + cfma.rn.f32 %f4, %f1, %f2, %f3;-fp-mode precisefma.rn.f32 %f4, %f1, %f2, %f3;— byte-identical to default-fp-mode precise -Xnvrtc --fmad=falsemul.rn.f32+add.rn.f32cmpreports the default andprecisePTX files as identical, soprecisecurrently has no effect whatsoever on this path.
Suggested fix
Pass
--fmad=falsefrom thePrecisecase inslang-nvrtc-compiler.cpp:--fmad=falsealone is the complete fix — verified. NVRTC's otherprecision-relevant defaults are already at their precise settings
(
--prec-div=true,--prec-sqrt=true,--ftz=false), so passing all fourproduces byte-identical PTX to passing only
--fmad=false(checked on a shaderexercising multiply-add, divide, sqrt and transcendentals). No other flag needs to
be added.
Why this is the right layer
The contraction decision belongs to the downstream compiler, and Slang already owns
the translation from
-fp-modeto downstream flags for every other backend(
-Gisfor DXC,D3DCOMPILE_IEEE_STRICTNESSfor FXC,/fp:precisefor MSVC).The NVRTC case is simply unimplemented rather than deliberately empty; there is no
IR- or emit-level work required, and no Slang-side representation question.
Limits of what was measured
accuracy difference contraction causes on real shaders is not quantified here —
only that the emitted instruction changes.
--fmad=falsewas not measured. Disabling contraction willcost some throughput; that is the expected and intended price of
precise, but areviewer may want a number before enabling it by default.
under both modes, so whatever their downstream compilers do about contraction is
likewise uncontrolled by
-fp-mode— worth a separate look, not covered here.fma.slangquoted above.Related
-fp-mode precisehas no effect on SPIR-V output (noNoContractionemitted) #11933 / Emit NoContraction under -fp-mode precise on direct SPIR-V #11935 — the same defect on SPIR-V:-fp-mode preciseemitted noNoContraction. Fixed by making the SPIR-V emitter honour the mode. This issue isthat fix's missing CUDA counterpart.
precisequalifier is ignored on SPIR-V: no NoContraction, and the expression is algebraically reassociated #12198 —precisequalifier ignored on SPIR-V. Adjacent: both are cases wherea precision request does not reach the code that would have to honour it.
-fp-mode defaultbehaving differently per target. Related in that eachbackend currently implements only one half of the
fast/preciseswitch; on NVRTCit is
fastthat is wired up andprecisethat is empty.Found while establishing what
-fp-modeactually switches on each target; theverification that
--fmad=falsealone suffices was done by comparing PTX with allfour precision flags against PTX with only that one.