Summary
rocjitsu executes the guest's MODE.FP_ROUND by setting the host's rounding mode — fp_mode.h wraps each architectural operation in a ScopedFenv (feholdexcept + fesetround) and lets the hardware round.
Nothing tells the compiler that rounding is dynamic, so it assumes the default mode and is free to move the arithmetic across the fesetround. Every directed rounding mode silently produces the round-to-nearest answer.
Reproducer
V_FMAC_F64 computing 1.0 * 2^-53 + 1.0 — an exact tie, half an ulp above 1.0, so the rounding mode alone decides the result.
MODE.FP_ROUND |
guest should see |
scalar path returns |
| 0 nearest-even |
0x3ff0000000000000 |
0x3ff0000000000000 ✅ |
| 1 +inf |
0x3ff0000000000001 |
0x3ff0000000000000 ❌ |
| 2 −inf / 3 zero |
0x3ff0000000000000 |
wrong on operands past the halfway point ❌ |
Independent of the test suite — a program calling fp_mode::fma_f64 with inputs taken from argv so nothing can be constant-folded:
$ g++-13 -O3 … round=1 -> 0x3ff0000000000000 # wrong
$ g++-13 -O3 -frounding-math … round=1 -> 0x3ff0000000000001 # correct
Not compiler-specific
Measured on the same probe, wrong without -frounding-math and correct with it, on all of:
- GCC 13.4
- clang 14
- clang 15
- AMD clang 22 (
/opt/rocm/llvm/bin/clang++)
So building with amdclang does not avoid it.
Impact
Any guest code that sets a directed rounding mode gets nearest-even instead. The scalar and SIMD paths are affected differently, so a result can also depend on RJ_FORCE_SCALAR.
It currently shows up as three failing tests on develop:
Vop2FmaF64SimdCorrectness.AllRoundAndDenormModesMatchScalar
Vop2FmaF64SimdCorrectness.SpecialValuesAndDenormBoundariesMatchScalarExactly
Gfx1250ExecutionTest.FusedOperationsHonorF16F64ModeControls
Those assert simd == scalar, which catches the divergence but names whichever path it is handed as the reference — here the scalar path is the wrong one, so the failure reads as "the two disagree" rather than "one is wrong".
Note
This is distinct from #10330, which is about scalar and SIMD matrix kernels accumulating MACs differently. Same visible symptom (a result depending on RJ_FORCE_SCALAR), different cause.
Summary
rocjitsuexecutes the guest'sMODE.FP_ROUNDby setting the host's rounding mode —fp_mode.hwraps each architectural operation in aScopedFenv(feholdexcept+fesetround) and lets the hardware round.Nothing tells the compiler that rounding is dynamic, so it assumes the default mode and is free to move the arithmetic across the
fesetround. Every directed rounding mode silently produces the round-to-nearest answer.Reproducer
V_FMAC_F64computing1.0 * 2^-53 + 1.0— an exact tie, half an ulp above 1.0, so the rounding mode alone decides the result.MODE.FP_ROUND0x3ff00000000000000x3ff0000000000000✅0x3ff00000000000010x3ff0000000000000❌0x3ff0000000000000Independent of the test suite — a program calling
fp_mode::fma_f64with inputs taken fromargvso nothing can be constant-folded:Not compiler-specific
Measured on the same probe, wrong without
-frounding-mathand correct with it, on all of:/opt/rocm/llvm/bin/clang++)So building with amdclang does not avoid it.
Impact
Any guest code that sets a directed rounding mode gets nearest-even instead. The scalar and SIMD paths are affected differently, so a result can also depend on
RJ_FORCE_SCALAR.It currently shows up as three failing tests on
develop:Those assert
simd == scalar, which catches the divergence but names whichever path it is handed as the reference — here the scalar path is the wrong one, so the failure reads as "the two disagree" rather than "one is wrong".Note
This is distinct from #10330, which is about scalar and SIMD matrix kernels accumulating MACs differently. Same visible symptom (a result depending on
RJ_FORCE_SCALAR), different cause.