Skip to content

SPIR-V: declare FloatControls2 for kernels with SPV_KHR_float_controls2 - #1007

Open
vchuravy wants to merge 1 commit into
mainfrom
vc/spirv-float-controls2
Open

vchuravy wants to merge 1 commit into
mainfrom
vc/spirv-float-controls2

Conversation

@vchuravy

@vchuravy vchuravy commented Oct 11, 2026 •

Copy link
Copy Markdown
Member

With extensions = "+SPV_KHR_float_controls2", the LLVM SPIR-V back-end emits per-instruction FPFastMathMode AllowContract for contract operations, which is how muladd can reach drivers as an FMA (JuliaGPU/KernelAbstractions.jl#865). KA and OpenCL.jl are about to enable the extension when the device reports it. With the extension enabled, though, most float kernels currently fail validation:

error: line 11: Operand 2 of ExecutionModeId requires one of these capabilities: FloatControls2
  OpExecutionModeId %kernel FPFastMathDefault %float %35

Cause (LLVM bug, still on main). SPIRVAsmPrinter replaces the ContractionOff it gives kernels with an implicit FPFastMathDefault (no flags) for every FP type in the module. SPIRVModuleAnalysis only registers the FloatControls2 capability and the extension for explicit spirv.ExecutionMode metadata, or when it decorates an instruction. So any kernel with float types but no contract operation is invalid. That includes a float type that appears only in the signature or inside a vector.

Fix. After the LLVM back-end runs, if the binary uses FPFastMathDefault but lacks OpCapability FloatControls2 or OpExtension "SPV_KHR_float_controls2", insert them. Otherwise the module is exactly what the back-end meant to emit; the patch is a no-op without the extension.

Declaring the defaults through spirv.ExecutionMode metadata looks cleaner, but it hits a second back-end bug. Once a function has explicit FPFastMathDefault info, handleMIFlagDecoration looks up the type of every instruction that can take fast-math flags, and for some OpExtInsts it gets operand 1 (the result type). The back-end segfaulted that way in addDecorations on GPUArrays' accumulate kernels. Only the :llvm back-end is affected; the Khronos translator declares what it needs.

Tests. A new "float controls" testset covers both back-ends, with the extension enabled:

  • a muladd kernel, which must get AllowContract;
  • a strict float kernel;
  • an integer kernel;
  • a kernel with a float only in its signature;
  • one with a float vector in its signature.

All are validated by spirv-val. With the patch disabled, the strict, signature and vector cases fail on :llvm. test/runtests.jl spirv: 733/733 pass.

End to end, with a PoCL build that accepts the extension (pocl/pocl#2393, LLVM 23.1.1) and the KA/OpenCL.jl PRs:

  • KA's suite matches main on the same PoCL, plus the new testset. One testset is excluded on both: "CPU back-end", where PoCL 8.0-pre with LLVM 23 aborts with Could not find a dominating alternative variable regardless of this change.
  • OpenCL.jl's full suite gives 30,515 passes versus 30,509 on main. The extra 6 are the new tests. The same two intrinsics crashes occur on both: illegal instructions from that PoCL build.
  • Before this fix, both suites failed spirv-val on most float kernels.

Both LLVM bugs should also be fixed upstream; this works around the first and avoids the second.

🤖 Generated with Claude Code

With SPV_KHR_float_controls2, the LLVM back-end replaces the
ContractionOff execution mode it gives kernels by FPFastMathDefault (with
no flags) for every floating-point type in the module. It only declares
the FloatControls2 capability and the extension that mode needs when it
decorates an instruction with fast-math flags, though, so a kernel with
floating-point types but without `contract` operations fails
validation:

    Operand 2 of ExecutionModeId requires one of these capabilities:
    FloatControls2

Add the missing declarations to the binary the back-end generates.
Declaring the defaults through `spirv.ExecutionMode` metadata instead
makes the back-end account for them, but then crashes it in
`handleMIFlagDecoration` on some `OpExtInst`s.

Assisted-by: Claude Code (Opus 5.5)
@codecov

codecov Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.54%. Comparing base (9f7a9e7) to head (2584c44).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1007      +/-   ##
==========================================
+ Coverage   89.49%   89.54%   +0.05%     
==========================================
  Files          31       31              
  Lines        6902     6937      +35     
==========================================
+ Hits         6177     6212      +35     
  Misses        725      725              

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@maleadt

maleadt commented Oct 11, 2026

Copy link
Copy Markdown
Member

Cause (LLVM bug, still on main). SPIRVAsmPrinter replaces the ContractionOff it gives kernels with an implicit FPFastMathDefault (no flags) for every FP type in the module. SPIRVModuleAnalysis only registers the FloatControls2 capability and the extension for explicit spirv.ExecutionMode metadata, or when it decorates an instruction. So any kernel with float types but no contract operation is invalid. That includes a float type that appears only in the signature or inside a vector.

Shouldn't we fix this on LLVM instead and backport the fix to our SPIR-V back-end?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants