Skip to content

[pocl] Drop dead deferred-codegen bodies before SPIR-V translation - #736

Draft
vchuravy wants to merge 1 commit into
mainfrom
vc/pocl-dce-deferred
Draft

[pocl] Drop dead deferred-codegen bodies before SPIR-V translation#736
vchuravy wants to merge 1 commit into
mainfrom
vc/pocl-dce-deferred

Conversation

@vchuravy

Copy link
Copy Markdown
Member

Companion to EnzymeAD/Enzyme.jl#3405. Needed to make Enzyme work over the POCL backend (x-ref #583, EnzymeAD/Enzyme.jl#3309). Standalone — does not depend on #583.

Problem

Deferred-codegen entrypoints — notably the wrappers Enzyme generates — are held externally live across GPUCompiler's InternalizePass so linking can resolve them. Once linked and alwaysinlined they are dead, but external linkage keeps GlobalDCEPass from dropping them, so the SPIR-V backend still has to translate a function it cannot express.

These wrappers take first-class aggregates containing addrspace(1) pointers. Extracting one back out miscompiles — the backend types the struct member as pointer-to-uchar but the extract result as pointer-to-double:

error: Result type (OpTypePointer) does not match the type that results from indexing into the composite (OpTypePointer).
  %105 = OpCompositeExtract %_ptr_CrossWorkgroup_double %91 0

This reproduces with no Enzyme involved:

target datalayout = "e-i64:64-...-G1"
target triple = "spirv64-unknown-unknown"

define spir_func void @extract_from_byval_struct({ ptr addrspace(1), i64 } %arg) {
  %p = extractvalue { ptr addrspace(1), i64 } %arg, 0
  store double 1.0, ptr addrspace(1) %p, align 8
  ret void
}

so the real fix belongs upstream in the LLVM SPIR-V backend. Setting spir_func on the wrapper is not sufficient — I checked.

This change

Empty the bodies of unreferenced non-kernel definitions in finish_ir!, so only declarations reach the backend. finish_ir! is the right hook because it runs after AlwaysInlinerPass (which is what makes these functions dead) and after the cleanup GlobalDCEPass that would otherwise have handled them. We empty rather than erase so the finish_ir! loop over the remaining deferred jobs can still look them up by name.

Guarded on job.config.kernel so it runs once for the entry job, not again for each deferred job.

Result

With this plus EnzymeAD/Enzyme.jl#3405, forward-mode Enzyme over a POCL kernel compiles, runs on device and produces correct derivatives:

KA POCL FORWARD-MODE ENZYME: PASS  (dA[1:4] = [2.0, 4.0, 6.0, 8.0])

Reverse mode remains blocked on a separate LLVM SPIR-V backend segfault in SPIRVInstructionSelector::selectExtractVal; see the Enzyme PR for the minimal reproducer.

🤖 Generated with Claude Code

Deferred-codegen entrypoints -- notably the wrappers Enzyme generates -- are
held externally live across GPUCompiler's `InternalizePass` so that linking can
resolve them. Once linked and `alwaysinline`d they are dead, but external
linkage keeps `GlobalDCEPass` from dropping them, so the SPIR-V backend still
has to translate a function it cannot express: they take first-class aggregates
containing `addrspace(1)` pointers, and extracting one back out miscompiles.
The backend types the struct member as pointer-to-uchar but the extract result
as pointer-to-double, so `spirv-val` rejects the module:

    error: Result type (OpTypePointer) does not match the type that results
    from indexing into the composite (OpTypePointer).
      %105 = OpCompositeExtract %_ptr_CrossWorkgroup_double %91 0

Reproduces without Enzyme in eight lines of IR, so the real fix belongs
upstream. Until then, empty the bodies of unreferenced non-kernel definitions in
`finish_ir!` so only declarations reach the backend. We empty rather than erase
so the `finish_ir!` loop over the remaining deferred jobs can still look them up
by name.

With this plus EnzymeAD/Enzyme.jl#3309, forward-mode Enzyme over a POCL kernel
compiles, runs and produces correct derivatives.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Show table
main c1fce3b... main / c1fce3b...
saxpy/default/Float32/1024 0.0866 ± 0.011 ms 0.0864 ± 0.011 ms 1 ± 0.17
saxpy/default/Float32/1048576 0.527 ± 0.028 ms 0.535 ± 0.033 ms 0.984 ± 0.081
saxpy/default/Float32/16384 0.0697 ± 0.034 ms 0.0707 ± 0.035 ms 0.986 ± 0.68
saxpy/default/Float32/2048 0.0858 ± 0.026 ms 0.0871 ± 0.024 ms 0.985 ± 0.4
saxpy/default/Float32/256 0.0868 ± 0.01 ms 0.0878 ± 0.011 ms 0.988 ± 0.17
saxpy/default/Float32/262144 0.183 ± 0.032 ms 0.185 ± 0.032 ms 0.991 ± 0.24
saxpy/default/Float32/32768 0.0763 ± 0.032 ms 0.0768 ± 0.032 ms 0.994 ± 0.58
saxpy/default/Float32/4096 0.0816 ± 0.032 ms 0.0878 ± 0.032 ms 0.929 ± 0.5
saxpy/default/Float32/512 0.0854 ± 0.0095 ms 0.0871 ± 0.01 ms 0.98 ± 0.16
saxpy/default/Float32/64 0.0868 ± 0.012 ms 0.0872 ± 0.012 ms 0.996 ± 0.19
saxpy/default/Float32/65536 0.0937 ± 0.032 ms 0.0927 ± 0.031 ms 1.01 ± 0.48
saxpy/default/Float64/1024 0.0862 ± 0.013 ms 0.0869 ± 0.013 ms 0.991 ± 0.21
saxpy/default/Float64/1048576 0.697 ± 0.11 ms 0.69 ± 0.14 ms 1.01 ± 0.26
saxpy/default/Float64/16384 0.0686 ± 0.03 ms 0.0724 ± 0.031 ms 0.947 ± 0.58
saxpy/default/Float64/2048 0.0843 ± 0.03 ms 0.0872 ± 0.026 ms 0.967 ± 0.45
saxpy/default/Float64/256 0.0863 ± 0.0091 ms 0.0879 ± 0.01 ms 0.982 ± 0.16
saxpy/default/Float64/262144 0.2 ± 0.037 ms 0.2 ± 0.036 ms 1 ± 0.26
saxpy/default/Float64/32768 0.079 ± 0.031 ms 0.0806 ± 0.03 ms 0.981 ± 0.53
saxpy/default/Float64/4096 0.0824 ± 0.029 ms 0.0857 ± 0.03 ms 0.961 ± 0.48
saxpy/default/Float64/512 0.0866 ± 0.011 ms 0.0871 ± 0.01 ms 0.995 ± 0.17
saxpy/default/Float64/64 0.0869 ± 0.013 ms 0.0869 ± 0.011 ms 1 ± 0.2
saxpy/default/Float64/65536 0.101 ± 0.032 ms 0.0975 ± 0.031 ms 1.04 ± 0.47
saxpy/static workgroup=(1024,)/Float32/1024 0.0835 ± 0.0094 ms 0.084 ± 0.0098 ms 0.994 ± 0.16
saxpy/static workgroup=(1024,)/Float32/1048576 0.445 ± 0.025 ms 0.448 ± 0.025 ms 0.994 ± 0.078
saxpy/static workgroup=(1024,)/Float32/16384 0.0662 ± 0.032 ms 0.0666 ± 0.032 ms 0.993 ± 0.68
saxpy/static workgroup=(1024,)/Float32/2048 0.0824 ± 0.026 ms 0.0836 ± 0.025 ms 0.985 ± 0.43
saxpy/static workgroup=(1024,)/Float32/256 0.084 ± 0.0093 ms 0.0844 ± 0.013 ms 0.995 ± 0.19
saxpy/static workgroup=(1024,)/Float32/262144 0.162 ± 0.033 ms 0.16 ± 0.033 ms 1.01 ± 0.29
saxpy/static workgroup=(1024,)/Float32/32768 0.0708 ± 0.03 ms 0.0718 ± 0.03 ms 0.986 ± 0.59
saxpy/static workgroup=(1024,)/Float32/4096 0.0804 ± 0.029 ms 0.0834 ± 0.03 ms 0.964 ± 0.49
saxpy/static workgroup=(1024,)/Float32/512 0.0832 ± 0.0091 ms 0.0849 ± 0.0086 ms 0.981 ± 0.15
saxpy/static workgroup=(1024,)/Float32/64 0.0834 ± 0.01 ms 0.0841 ± 0.012 ms 0.992 ± 0.19
saxpy/static workgroup=(1024,)/Float32/65536 0.0848 ± 0.032 ms 0.0853 ± 0.032 ms 0.994 ± 0.52
saxpy/static workgroup=(1024,)/Float64/1024 0.0838 ± 0.015 ms 0.0838 ± 0.013 ms 1 ± 0.24
saxpy/static workgroup=(1024,)/Float64/1048576 0.546 ± 0.073 ms 0.55 ± 0.086 ms 0.992 ± 0.2
saxpy/static workgroup=(1024,)/Float64/16384 0.0653 ± 0.029 ms 0.0681 ± 0.029 ms 0.96 ± 0.6
saxpy/static workgroup=(1024,)/Float64/2048 0.0816 ± 0.03 ms 0.0847 ± 0.028 ms 0.964 ± 0.48
saxpy/static workgroup=(1024,)/Float64/256 0.084 ± 0.0098 ms 0.0842 ± 0.008 ms 0.998 ± 0.15
saxpy/static workgroup=(1024,)/Float64/262144 0.186 ± 0.036 ms 0.189 ± 0.034 ms 0.981 ± 0.26
saxpy/static workgroup=(1024,)/Float64/32768 0.0745 ± 0.03 ms 0.0757 ± 0.028 ms 0.985 ± 0.54
saxpy/static workgroup=(1024,)/Float64/4096 0.0794 ± 0.028 ms 0.0818 ± 0.03 ms 0.971 ± 0.5
saxpy/static workgroup=(1024,)/Float64/512 0.0842 ± 0.01 ms 0.0846 ± 0.009 ms 0.995 ± 0.16
saxpy/static workgroup=(1024,)/Float64/64 0.0841 ± 0.012 ms 0.0845 ± 0.013 ms 0.995 ± 0.21
saxpy/static workgroup=(1024,)/Float64/65536 0.0946 ± 0.032 ms 0.0924 ± 0.03 ms 1.02 ± 0.48
time_to_load 1.07 ± 0.025 s 1.06 ± 0.012 s 1.01 ± 0.026

Benchmark Plots

A plot of the benchmark results have been uploaded as an artifact to the workflow run for this PR.
Go to "Actions"->"Benchmark a pull request"->[the most recent run]->"Artifacts" (at the bottom).

@codecov

codecov Bot commented Jul 27, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 64.98%. Comparing base (65690fd) to head (c1fce3b).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #736      +/-   ##
==========================================
+ Coverage   64.77%   64.98%   +0.20%     
==========================================
  Files          24       24              
  Lines        2024     2036      +12     
==========================================
+ Hits         1311     1323      +12     
  Misses        713      713              

☔ 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.

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.

1 participant