Skip to content

POCL: contract fast-math flags lost through SPIR-V, no FMAs emitted #865

Open

Description

@vchuravy

Julia's contract flags don't survive the SPIR-V path in the POCL backend, so PoCL emits separate vmulsd/vaddsd instead of FMAs. Seen in Trixi.jl's KA kernels (trixi-framework/Trixi.jl#3321). A prototype fusing mul+add into llvm.fmuladd before SPIR-V generation gave 2–9% speedups. Options: emit llvm.fmuladd pre-translation, or carry contraction via SPIR-V FPFastMathMode. Related: #860, #862.

Activity

  1. vchuravy commented on Oct 11, 2026

    @vchuravy
    MemberAuthor

    MWE:

    using KernelAbstractions
    import KernelAbstractions.POCL as P
    
    @kernel function axpy!(y, @Const(a), @Const(x))
        i = @index(Global)
        @inbounds y[i] = muladd(a[i], x[i], y[i])
    end
    y = rand(1024); a = rand(1024); x = rand(1024)
    k = axpy!(POCLBackend(), 64)
    P.@device_code_llvm   k(y, a, x; ndrange=1024)   # fmul contract + fadd contract
    P.@device_code_native k(y, a, x; ndrange=1024)   # OpFMul + OpFAdd, entry point has ContractionOff

    With POCL_CACHE_DIR set, PoCL's .so.o contains vmulsd/vaddsd (and vmulpd/vaddpd), no vfmadd.

    There are two causes:

    1. Base SPIR-V can't carry the per-instruction contract flag. AllowContract only exists in SPV_KHR_float_controls2.
    2. The LLVM SPIR-V back-end adds ContractionOff to every kernel entry point unless the module has !opencl.enable.FP_CONTRACT (SPIRVAsmPrinter::outputExecutionMode). Clang emits that metadata for OpenCL C; GPUCompiler doesn't.

    I tested these variants with a hook in POCL's compiler job (SPIRV_LLVM_Backend_jll 23.1.1, pocl_standalone_jll, x86 with FMA):

    Variant SPIR-V PoCL x86
    baseline FMul + FAdd, ContractionOff vmulsd/vaddsd
    add !opencl.enable.FP_CONTRACT FMul + FAdd, no ContractionOff still vmulsd/vaddsd
    fuse fmul contract + fadd contract into llvm.fmuladd before codegen OpExtInst fma vfmadd213sd, vfmadd231pd

    The metadata alone doesn't help. PoCL won't contract plain fmul/fadd, and it would also be the wrong semantics, since it allows contracting any fmul/fadd.

    Proposed fix: run an IR pass in GPUCompiler's SPIR-V finish_ir! (next to lower_minimum_maximum!) that fuses single-use contract-flagged fmul + fadd/fsub into llvm.fmuladd. This fixes all SPIR-V consumers (OpenCL.jl, oneAPI.jl, POCL). One caveat: the back-end lowers fmuladd to OpenCL.std fma, not mad, which is slow on devices without hardware FMA.

    Longer term, emit SPV_KHR_float_controls2 (FPFastMathMode AllowContract) and have PoCL accept it. I'm looking into the PoCL side.

  2. vchuravy commented on Oct 11, 2026

    @vchuravy
    MemberAuthor

    PoCL side: pocl/pocl#2393 (draft) enables SPV_KHR_float_controls2 for the CPU devices with LLVM >= 23. With it, FPFastMathMode AllowContract reaches PoCL as contract and a test kernel compiles to vfmadd. To use this from KA, GPUCompiler still needs to pass +SPV_KHR_float_controls2 to the SPIR-V back-end when the device reports it. I haven't yet checked that the back-end then emits AllowContract on each instruction. Until then, the llvm.fmuladd fusion above is the fix that works today.

  3. added this to the 0.10.1 milestone on Oct 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions