Skip to content

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

Closed
vchuravy wants to merge 6 commits into
mainfrom
vc/enzyme-spirv
Closed

[pocl] Drop dead deferred-codegen bodies before SPIR-V translation#735
vchuravy wants to merge 6 commits into
mainfrom
vc/enzyme-spirv

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

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

vchuravy and others added 6 commits July 20, 2026 14:27
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>
@vchuravy

Copy link
Copy Markdown
Member Author

Superseded — branch accidentally included #583's commits. Reopened as a standalone PR off main.

@vchuravy vchuravy closed this Jul 27, 2026
@vchuravy
vchuravy deleted the vc/enzyme-spirv branch July 27, 2026 09:57
@github-actions

Copy link
Copy Markdown
Contributor

Benchmark Results

Show table
main e3e4799... main / e3e4799...
saxpy/default/Float32/1024 0.0734 ± 0.03 ms 0.0746 ± 0.031 ms 0.983 ± 0.58
saxpy/default/Float32/1048576 0.494 ± 0.022 ms 0.493 ± 0.023 ms 1 ± 0.064
saxpy/default/Float32/16384 0.061 ± 0.03 ms 0.063 ± 0.03 ms 0.968 ± 0.67
saxpy/default/Float32/2048 0.0607 ± 0.029 ms 0.0589 ± 0.029 ms 1.03 ± 0.7
saxpy/default/Float32/256 0.0474 ± 0.031 ms 0.0515 ± 0.032 ms 0.921 ± 0.83
saxpy/default/Float32/262144 0.17 ± 0.029 ms 0.174 ± 0.029 ms 0.975 ± 0.23
saxpy/default/Float32/32768 0.0687 ± 0.029 ms 0.0691 ± 0.029 ms 0.993 ± 0.59
saxpy/default/Float32/4096 0.0614 ± 0.027 ms 0.0682 ± 0.027 ms 0.901 ± 0.53
saxpy/default/Float32/512 0.0496 ± 0.033 ms 0.069 ± 0.031 ms 0.719 ± 0.58
saxpy/default/Float32/64 0.0453 ± 0.028 ms 0.0491 ± 0.031 ms 0.922 ± 0.82
saxpy/default/Float32/65536 0.0854 ± 0.03 ms 0.0839 ± 0.03 ms 1.02 ± 0.51
saxpy/default/Float64/1024 0.0684 ± 0.031 ms 0.0717 ± 0.031 ms 0.954 ± 0.6
saxpy/default/Float64/1048576 0.578 ± 0.093 ms 0.564 ± 0.075 ms 1.02 ± 0.21
saxpy/default/Float64/16384 0.0599 ± 0.025 ms 0.0605 ± 0.026 ms 0.989 ± 0.6
saxpy/default/Float64/2048 0.0545 ± 0.031 ms 0.0564 ± 0.03 ms 0.967 ± 0.75
saxpy/default/Float64/256 0.0465 ± 0.031 ms 0.0556 ± 0.032 ms 0.835 ± 0.74
saxpy/default/Float64/262144 0.19 ± 0.036 ms 0.195 ± 0.033 ms 0.977 ± 0.25
saxpy/default/Float64/32768 0.0724 ± 0.028 ms 0.0713 ± 0.027 ms 1.02 ± 0.56
saxpy/default/Float64/4096 0.0711 ± 0.025 ms 0.0712 ± 0.024 ms 0.999 ± 0.49
saxpy/default/Float64/512 0.0503 ± 0.034 ms 0.0684 ± 0.03 ms 0.735 ± 0.59
saxpy/default/Float64/64 0.0455 ± 0.029 ms 0.0481 ± 0.031 ms 0.946 ± 0.85
saxpy/default/Float64/65536 0.089 ± 0.029 ms 0.091 ± 0.029 ms 0.978 ± 0.44
saxpy/static workgroup=(1024,)/Float32/1024 0.0677 ± 0.031 ms 0.0703 ± 0.031 ms 0.964 ± 0.61
saxpy/static workgroup=(1024,)/Float32/1048576 0.422 ± 0.029 ms 0.424 ± 0.028 ms 0.994 ± 0.094
saxpy/static workgroup=(1024,)/Float32/16384 0.056 ± 0.028 ms 0.0582 ± 0.03 ms 0.963 ± 0.68
saxpy/static workgroup=(1024,)/Float32/2048 0.0541 ± 0.029 ms 0.0553 ± 0.029 ms 0.979 ± 0.73
saxpy/static workgroup=(1024,)/Float32/256 0.0467 ± 0.03 ms 0.0492 ± 0.029 ms 0.95 ± 0.82
saxpy/static workgroup=(1024,)/Float32/262144 0.145 ± 0.029 ms 0.151 ± 0.029 ms 0.961 ± 0.27
saxpy/static workgroup=(1024,)/Float32/32768 0.0609 ± 0.025 ms 0.0631 ± 0.026 ms 0.964 ± 0.57
saxpy/static workgroup=(1024,)/Float32/4096 0.0561 ± 0.027 ms 0.0662 ± 0.026 ms 0.847 ± 0.53
saxpy/static workgroup=(1024,)/Float32/512 0.0486 ± 0.03 ms 0.065 ± 0.029 ms 0.748 ± 0.57
saxpy/static workgroup=(1024,)/Float32/64 0.046 ± 0.03 ms 0.0473 ± 0.029 ms 0.972 ± 0.86
saxpy/static workgroup=(1024,)/Float32/65536 0.0733 ± 0.029 ms 0.0754 ± 0.03 ms 0.972 ± 0.54
saxpy/static workgroup=(1024,)/Float64/1024 0.0548 ± 0.031 ms 0.0656 ± 0.031 ms 0.836 ± 0.62
saxpy/static workgroup=(1024,)/Float64/1048576 0.54 ± 0.097 ms 0.553 ± 0.093 ms 0.976 ± 0.24
saxpy/static workgroup=(1024,)/Float64/16384 0.0557 ± 0.024 ms 0.0595 ± 0.027 ms 0.937 ± 0.58
saxpy/static workgroup=(1024,)/Float64/2048 0.0519 ± 0.031 ms 0.0542 ± 0.029 ms 0.958 ± 0.77
saxpy/static workgroup=(1024,)/Float64/256 0.0472 ± 0.03 ms 0.0537 ± 0.029 ms 0.878 ± 0.73
saxpy/static workgroup=(1024,)/Float64/262144 0.185 ± 0.032 ms 0.193 ± 0.033 ms 0.959 ± 0.23
saxpy/static workgroup=(1024,)/Float64/32768 0.0668 ± 0.025 ms 0.069 ± 0.027 ms 0.967 ± 0.52
saxpy/static workgroup=(1024,)/Float64/4096 0.0582 ± 0.026 ms 0.0664 ± 0.026 ms 0.876 ± 0.52
saxpy/static workgroup=(1024,)/Float64/512 0.0483 ± 0.031 ms 0.0643 ± 0.029 ms 0.751 ± 0.59
saxpy/static workgroup=(1024,)/Float64/64 0.0458 ± 0.029 ms 0.0473 ± 0.028 ms 0.969 ± 0.84
saxpy/static workgroup=(1024,)/Float64/65536 0.0854 ± 0.025 ms 0.088 ± 0.027 ms 0.97 ± 0.41
time_to_load 0.928 ± 0.0079 s 0.927 ± 0.01 s 1 ± 0.014

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

❌ Patch coverage is 0% with 33 lines in your changes missing coverage. Please review.
✅ Project coverage is 0.83%. Comparing base (65690fd) to head (e3e4799).

Files with missing lines Patch % Lines
src/pocl/compiler/compilation.jl 0.00% 12 Missing ⚠️
ext/EnzymeCore07Ext.jl 0.00% 9 Missing ⚠️
ext/EnzymeCore08Ext.jl 0.00% 9 Missing ⚠️
ext/EnzymeExt.jl 0.00% 3 Missing ⚠️

❗ There is a different number of reports uploaded between BASE (65690fd) and HEAD (e3e4799). Click for more details.

HEAD has 28 uploads less than BASE
Flag BASE (65690fd) HEAD (e3e4799)
52 24
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #735       +/-   ##
==========================================
- Coverage   64.77%   0.83%   -63.94%     
==========================================
  Files          24      23        -1     
  Lines        2024    1788      -236     
==========================================
- Hits         1311      15     -1296     
- Misses        713    1773     +1060     

☔ 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