Skip to content

# [Vulkan] Precise FMA-based compensated summation is wrong unless the correction helper is noinline #12285

Description

@ccummingsNV

slang-noinline-repro.zip

Summary

On an NVIDIA GB300 with driver 610.43.02, a small Neumaier compensated-summation
shader returns the wrong result through Vulkan when its correction helper is
inlined. Adding [noinline] to the helper makes the same shader return the
expected value. The CUDA target returns the expected value both with and
without [noinline].

The Slang session uses SlangFloatingPointMode.precise. Slang's emitted SPIR-V
still contains the expected OpFAdd followed by GLSL.std.450 Fma for the
inlined case, but it has no NoContraction decorations. This may therefore be
a Slang precise-mode semantics/code-generation issue or a downstream NVIDIA
Vulkan compiler issue. I would appreciate guidance on which layer owns it and
how an application should express this arithmetic requirement without using a
function-boundary workaround.

Expected result

For the input sequence [16777216.0, 1.0, -16777216.0], both implementations
should return the compensated average 0.33333334.

Actual result

Vulkan:

backend:  vulkan
expected: np.float32(0.33333334)
inline:   np.float32(0.0)
noinline: np.float32(0.33333334)

CUDA control:

backend:  cuda
expected: np.float32(0.33333334)
inline:   np.float32(0.33333334)
noinline: np.float32(0.33333334)

Reproduction

The attached repro.slang has inline and noinline versions of the same helper.
The attached repro.py dispatches both variants and prints their outputs. It
requires SlangPy and NumPy.

python repro.py vulkan
python repro.py cuda

The script exits with status 1 when either output differs from the expected
value, so the Vulkan command is expected to exit 1 while the bug is present.

Readable SPIR-V can also be generated with:

slangc repro.slang -entry compute_main -target spirv-assembly \
    -profile glsl_450 -fp-mode precise -o repro-precise.spvasm

The inline path contains this operation order:

%next = OpFAdd %float %sum %value
%correction0 = OpExtInst %float %glsl Fma %float_n1 %next %sum
%correction = OpFAdd %float %correction0 %value

The noinline path is emitted as an OpFunction with DontInline and is called
with OpFunctionCall.

Environment

  • Slang 2026.12
  • SlangPy 0.41.0
  • Linux aarch64, Ubuntu kernel 6.8.0-124-generic
  • NVIDIA GB300
  • NVIDIA driver 610.43.02
  • CUDA toolkit 13.3, used only for the CUDA control
  • Python 3.12.3
  • NumPy 2.5.1

Workaround

Apply [noinline] to the correction helper for the Vulkan target. Falcor2 is
currently using this conditional workaround:

#ifdef __TARGET_VULKAN__
[noinline]
#endif
float neumaier_correction(float sum, float value, float next)

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions