JuliaGPU / JuliaGPU/GPUCompiler.jl
ABI handling for more than just specptr/jfptr
- Dominant language
- Julia
- Stars
- 187
- Forks
- 68
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 28
Description
Due to its heritage GPUCompiler currently only handles `specptr` and more recently `jfptr`.
In https://github.com/EnzymeAD/Enzyme.jl/issues/1134 Enzyme is encountering a expectation mismatch. Esentially we ask GPUCompiler to produce a function with `specptr` ABI and receive a function with ABI `japi`
```
using Statistics
function f(temp)
R_inv = [temp 0. 0. 0. 0. 0.;
0. temp 0. 0. 0. 0.;
0. 0. temp 0. 0. 0.;
0. 0. 0. temp 0. 0.;
0. 0. 0. 0. temp 0.;
]
return R_inv[1,1]
end
```
To exemplify the issue on 1.10-rc2 `hvcat` contains an `invoke` call. So trying to codegen the `f` function using GPUCompiler yields:
```
define double @julia_f_340(double %0) local_unnamed_addr {
....
%47 = call nonnull {}* @ijl_invoke({}* inttoptr (i64 140379636017936 to {}*), {}** nonnull %.sub, i32 31, {}* inttoptr (i64 140379798006736 to {}*))
....
```
Enzyme then goes and extracts the method instance of the call target (Enzyme free reproducer below) and GPUCompiler produces a
```
; @ abstractarray.jl:2114 within `hvcat`
define nonnull {}* @japi1_hvcat_800({}* %function, {}** noalias nocapture noundef readonly %args, i32 %nargs) local_unnamed_addr {
```
Enzyme most likely will need to handle the multiple ABI's, but I would prefer if there was a better way than just matching the name of the function. Julia internally has the calling convention during codegen and we may be able to put that in meta?
cc: @gbaraldi @wsmoses
## Enzyme free reproducer
```
using GPUCompiler
using Statistics
function f(temp)
R_inv = [temp 0. 0. 0. 0. 0.;
0. temp 0. 0. 0. 0.;
0. 0. temp 0. 0. 0.;
0. 0. 0. temp 0. 0.;
0. 0. 0. 0. temp 0.;
]
return R_inv[1,1]
end
module Native
using GPUCompiler
using ReTestItems
setup = include(joinpath(dirname(pathof(GPUCompiler)), "..", "test", "native_testsetup.jl"))
cd(dirname(setup.file)) do
eval(setup.code)
end
end
asm = sprint(io->Native.code_llvm(io, f, (Float64,)))
r_invoke = r"\@ijl_invoke\({}\* inttoptr \(i64 (\d*) to {}\*\), {}\*\* nonnull \%\.sub, i32 31, {}\* inttoptr \(i64 (\d*) to {}\*\)\)"
m = match(r_invoke, asm)
F = Base.unsafe_pointer_to_objref(Base.reinterpret(Ptr{Cvoid}, parse(UInt, m.captures[1])))
mi = Base.unsafe_pointer_to_objref(Base.reinterpret(Ptr{Cvoid}, parse(UInt, m.captures[2])))
target = Native.NativeCompilerTarget()
params = Native.CompilerParams(false, Native.test_method_table)
config = GPUCompiler.CompilerConfig(target, params; kernel=false, entry_abi=:specfunc, always_inline=false)
job = GPUCompiler.CompilerJob(mi, config)
GPUCompiler.code_llvm(job)
```
Contributor guide
No contributing guide indexed for this repository
Research direction
Start with the Enzyme free reproducer, then inspect Native.code_llvm, CompilerConfig with entry_abi=:specfunc, and GPUCompiler.code_llvm. Reproduce the japi output for the hvcat method instance and trace how ABI information is represented for specptr and jfptr. Done should include a way to handle additional ABIs without relying on function-name matching, with the reproducer or tests confirming the result.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- julia
- Domain
- backend-api-design, compilers
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 30/100