Skip to content

Commit 9f2c1bd

Browse files
vchuravyclaude
andauthored
GCN: generate machine code via external AMDGPU_LLVM_Backend_jll (#857)
Mirror the NVPTX_LLVM_Backend_jll approach for AMDGPU: override `mcgen` for `GCNCompilerTarget` to emit machine code through the external, up-to-date `llc` from AMDGPU_LLVM_Backend_jll instead of the in-process LLVM back-end. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 04e9aa9 commit 9f2c1bd

6 files changed

Lines changed: 117 additions & 4 deletions

File tree

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@ Tracy = "e689c965-62c8-4b79-b2c5-8359227902fd"
2222
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
2323

2424
[weakdeps]
25+
AMDGPU_LLVM_Backend_jll = "cc5c0156-bd05-5a77-8a68-bb0aafb29019"
2526
LLVMDowngrader_jll = "f52de702-fb25-5922-94ba-81dd59b07444"
2627
NVPTX_LLVM_Backend_jll = "ef6e0fe3-e6ef-59c0-bde6-4989574699e0"
2728

2829
[compat]
30+
AMDGPU_LLVM_Backend_jll = "22"
2931
ExprTools = "0.1"
3032
InteractiveUtils = "1"
3133
LLVM = "9.9"

src/gcn.jl

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
# implementation of the GPUCompiler interfaces for generating GCN code
22

3+
const AMDGPU_LLVM_Backend_jll =
4+
LazyModule("AMDGPU_LLVM_Backend_jll",
5+
UUID("cc5c0156-bd05-5a77-8a68-bb0aafb29019"))
6+
37
## target
48

59
export GCNCompilerTarget
610

711
Base.@kwdef struct GCNCompilerTarget <: AbstractCompilerTarget
812
dev_isa::String
913
features::String=""
14+
15+
backend::Symbol = isavailable(AMDGPU_LLVM_Backend_jll) ? :external : :inprocess
1016
end
11-
GCNCompilerTarget(dev_isa; features="") = GCNCompilerTarget(dev_isa, features)
17+
GCNCompilerTarget(dev_isa; kwargs...) = GCNCompilerTarget(; dev_isa, kwargs...)
1218

1319
llvm_triple(::GCNCompilerTarget) = "amdgcn-amd-amdhsa"
1420

@@ -35,7 +41,7 @@ end
3541

3642
# TODO: encode debug build or not in the compiler job
3743
# https://github.com/JuliaGPU/CUDAnative.jl/issues/368
38-
runtime_slug(job::CompilerJob{GCNCompilerTarget}) = "gcn-$(job.config.target.dev_isa)$(job.config.target.features)"
44+
runtime_slug(job::CompilerJob{GCNCompilerTarget}) = "gcn-$(job.config.target.dev_isa)$(job.config.target.features)-$(job.config.target.backend)"
3945

4046
const gcn_intrinsics = () # TODO: ("vprintf", "__assertfail", "malloc", "free")
4147
isintrinsic(::CompilerJob{GCNCompilerTarget}, fn::String) = in(fn, gcn_intrinsics)
@@ -153,6 +159,70 @@ function add_kernarg_address_spaces!(
153159
return new_f
154160
end
155161

162+
@unlocked function mcgen(@nospecialize(job::CompilerJob{GCNCompilerTarget}),
163+
mod::LLVM.Module, format=LLVM.API.LLVMAssemblyFile)
164+
target = job.config.target
165+
166+
if target.backend === :inprocess
167+
if :AMDGPU LLVM.backends()
168+
error("The in-process LLVM lacks the AMDGPU target; cannot compile to GCN. " *
169+
"Load AMDGPU_LLVM_Backend_jll and use `backend=:external` instead.")
170+
end
171+
return invoke(mcgen, Tuple{CompilerJob, LLVM.Module, typeof(format)},
172+
job, mod, format)
173+
elseif target.backend !== :external
174+
error("Unsupported GCN back-end $(repr(target.backend)); " *
175+
"expected :external or :inprocess.")
176+
end
177+
178+
if !isavailable(AMDGPU_LLVM_Backend_jll) || !AMDGPU_LLVM_Backend_jll.is_available()
179+
error("The :external GCN back-end requires AMDGPU_LLVM_Backend_jll, which " *
180+
"should be installed and loaded first.")
181+
end
182+
183+
filetype = if format == LLVM.API.LLVMAssemblyFile
184+
"asm"
185+
elseif format == LLVM.API.LLVMObjectFile
186+
"obj"
187+
else
188+
error("Unsupported GCN output format $format")
189+
end
190+
191+
input = tempname(cleanup=false) * ".bc"
192+
output = tempname(cleanup=false) * (filetype == "asm" ? ".s" : ".o")
193+
write(input, mod)
194+
195+
cmd = `$(AMDGPU_LLVM_Backend_jll.llc()) $input
196+
-mtriple=$(llvm_triple(target))
197+
-mcpu=$(target.dev_isa)
198+
-mattr=$(target.features)
199+
--relocation-model=pic
200+
-filetype=$filetype
201+
-o $output`
202+
out = Pipe()
203+
proc = run(pipeline(ignorestatus(cmd); stdout=out, stderr=out); wait=false)
204+
close(out.in)
205+
log = strip(read(out, String))
206+
wait(proc)
207+
if !success(proc)
208+
# keep the input around for debugging
209+
msg = "Failed to compile to GCN with external llc"
210+
isempty(log) || (msg *= ":\n" * log)
211+
msg *= "\nIf you think this is a bug, please file an issue and attach $(input)."
212+
isfile(output) && rm(output)
213+
error(msg)
214+
elseif !isempty(log)
215+
# llc only diagnoses on stderr; even successful compilation may e.g. have
216+
# ignored an unrecognized CPU or feature, so make sure this surfaces.
217+
@warn "External llc reported:\n$log"
218+
end
219+
220+
code = filetype == "asm" ? read(output, String) : String(read(output))
221+
rm(input)
222+
rm(output)
223+
return code
224+
end
225+
156226

157227
## LLVM passes
158228

test/Project.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[deps]
2+
AMDGPU_LLVM_Backend_jll = "cc5c0156-bd05-5a77-8a68-bb0aafb29019"
23
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
34
FileCheck = "4e644321-382b-4b05-b0b6-5d23c3d944fb"
45
GPUCompiler = "61eb1bfa-7361-4325-ad38-22787b887f55"

test/gcn.jl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,40 @@ if :AMDGPU in LLVM.backends()
33
# XXX: generic `sink` generates an instruction selection error
44
sink_gcn(i) = sink(i, Val(5))
55

6+
@testset "backend selector" begin
7+
# in the test environment AMDGPU_LLVM_Backend_jll is loaded, so the default is :external
8+
@test GCNCompilerTarget(dev_isa="gfx900").backend === :external
9+
10+
# both constructor forms accept an explicit backend, alongside the other options
11+
@test GCNCompilerTarget(dev_isa="gfx900"; backend=:inprocess).backend === :inprocess
12+
@test GCNCompilerTarget("gfx900"; backend=:inprocess).backend === :inprocess
13+
let target = GCNCompilerTarget("gfx900"; features="+wavefrontsize64", backend=:external)
14+
@test target.dev_isa == "gfx900"
15+
@test target.features == "+wavefrontsize64"
16+
@test target.backend === :external
17+
end
18+
19+
mod = @eval module $(gensym())
20+
kernel() = return
21+
end
22+
23+
# the backend participates in the runtime slug, so different back-ends don't share a cache
24+
job_ext, _ = GCN.create_job(mod.kernel, Tuple{}; backend=:external)
25+
job_inp, _ = GCN.create_job(mod.kernel, Tuple{}; backend=:inprocess)
26+
@test endswith(GPUCompiler.runtime_slug(job_ext), "-external")
27+
@test endswith(GPUCompiler.runtime_slug(job_inp), "-inprocess")
28+
@test GPUCompiler.runtime_slug(job_ext) != GPUCompiler.runtime_slug(job_inp)
29+
30+
# the explicit :external backend generates machine code through the external llc
31+
@test (GCN.code_native(devnull, mod.kernel, Tuple{}; backend=:external); true)
32+
33+
# the :inprocess backend generates machine code through the in-process LLVM back-end
34+
@test (GCN.code_native(devnull, mod.kernel, Tuple{}; backend=:inprocess); true)
35+
36+
# an unknown back-end is rejected at machine-code generation
37+
@test_throws "Unsupported GCN back-end" GCN.code_native(devnull, mod.kernel, Tuple{}; backend=:bogus)
38+
end
39+
640
@testset "IR" begin
741

842
@testset "kernel calling convention" begin

test/helpers/gcn.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ import ..TestRuntime
66
struct CompilerParams <: AbstractCompilerParams end
77
GPUCompiler.runtime_module(::CompilerJob{<:Any,CompilerParams}) = TestRuntime
88

9-
function create_job(@nospecialize(func), @nospecialize(types); kwargs...)
9+
function create_job(@nospecialize(func), @nospecialize(types); backend::Symbol=:external, kwargs...)
1010
config_kwargs, kwargs = split_kwargs(kwargs, GPUCompiler.CONFIG_KWARGS)
1111
source = methodinstance(typeof(func), Base.to_tuple_type(types), Base.get_world_counter())
12-
target = GCNCompilerTarget(dev_isa="gfx900")
12+
target = GCNCompilerTarget(dev_isa="gfx900"; backend)
1313
params = CompilerParams()
1414
config = CompilerConfig(target, params; kernel=false, config_kwargs...)
1515
CompilerJob(source, config), kwargs

test/runtests.jl

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,14 @@ import GPUCompiler, LLVM
33
using GPUCompiler, LLVM
44
using SPIRV_LLVM_Backend_jll, SPIRV_LLVM_Translator_jll, SPIRV_Tools_jll
55
using NVPTX_LLVM_Backend_jll
6+
using AMDGPU_LLVM_Backend_jll
67

78
const init_code = quote
89
using GPUCompiler, LLVM
910
using SPIRV_LLVM_Backend_jll, SPIRV_LLVM_Translator_jll, SPIRV_Tools_jll
1011
using LLVMDowngrader_jll
1112
using NVPTX_LLVM_Backend_jll
13+
using AMDGPU_LLVM_Backend_jll
1214

1315
# include all helpers
1416
include(joinpath(@__DIR__, "helpers", "runtime.jl"))
@@ -53,6 +55,10 @@ if filter_tests!(testsuite, args)
5355
startswith(key, "ptx") && delete!(testsuite, key)
5456
end
5557
end
58+
if !AMDGPU_LLVM_Backend_jll.is_available()
59+
@warn "AMDGPU back-end not available; skipping GCN tests"
60+
delete!(testsuite, "gcn")
61+
end
5662
end
5763

5864
runtests(GPUCompiler, args; testsuite, init_code)

0 commit comments

Comments
 (0)