Skip to content

Commit 30cd0ae

Browse files
authored
Simplify interfaces (#453)
1 parent 9351e5f commit 30cd0ae

8 files changed

Lines changed: 123 additions & 149 deletions

File tree

src/gcn.jl

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -35,26 +35,17 @@ runtime_slug(job::CompilerJob{GCNCompilerTarget}) = "gcn-$(job.config.target.dev
3535
const gcn_intrinsics = () # TODO: ("vprintf", "__assertfail", "malloc", "free")
3636
isintrinsic(::CompilerJob{GCNCompilerTarget}, fn::String) = in(fn, gcn_intrinsics)
3737

38-
function process_entry!(job::CompilerJob{GCNCompilerTarget}, mod::LLVM.Module, entry::LLVM.Function)
39-
entry = invoke(process_entry!, Tuple{CompilerJob, LLVM.Module, LLVM.Function}, job, mod, entry)
38+
function finish_module!(@nospecialize(job::CompilerJob{GCNCompilerTarget}),
39+
mod::LLVM.Module, entry::LLVM.Function)
40+
@dispose pm=ModulePassManager() begin
41+
add!(pm, ModulePass("LowerThrowExtra", lower_throw_extra!))
42+
run!(pm, mod)
43+
end
4044

4145
if job.config.kernel
4246
# calling convention
4347
callconv!(entry, LLVM.API.LLVMAMDGPUKERNELCallConv)
44-
end
45-
46-
entry
47-
end
4848

49-
function add_lowering_passes!(job::CompilerJob{GCNCompilerTarget}, pm::LLVM.PassManager)
50-
add!(pm, ModulePass("LowerThrowExtra", lower_throw_extra!))
51-
end
52-
53-
function finish_module!(@nospecialize(job::CompilerJob{GCNCompilerTarget}),
54-
mod::LLVM.Module, entry::LLVM.Function)
55-
entry = invoke(finish_module!, Tuple{CompilerJob, LLVM.Module, LLVM.Function}, job, mod, entry)
56-
57-
if job.config.kernel
5849
# work around bad byval codegen (JuliaGPU/GPUCompiler.jl#92)
5950
entry = lower_byval(job, mod, entry)
6051
end

src/interface.jl

Lines changed: 29 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ else
167167
end
168168

169169

170-
## interfaces and fallback definitions
170+
## default definitions that can be overridden to influence GPUCompiler's behavior
171171

172172
# Has the runtime available and does not require special handling
173173
uses_julia_runtime(@nospecialize(job::CompilerJob)) = false
@@ -201,9 +201,6 @@ can_safepoint(@nospecialize(job::CompilerJob)) = uses_julia_runtime(job)
201201
# the generated code of this compiler job (with exception of the function source)
202202
runtime_slug(@nospecialize(job::CompilerJob)) = error("Not implemented")
203203

204-
# early processing of the newly generated LLVM IR module
205-
process_module!(@nospecialize(job::CompilerJob), mod::LLVM.Module) = return
206-
207204
# the type of the kernel state object, or Nothing if this back-end doesn't need one.
208205
#
209206
# the generated code will be rewritten to include an object of this type as the first
@@ -214,51 +211,6 @@ kernel_state_type(@nospecialize(job::CompilerJob)) = Nothing
214211
# Does the target need to pass kernel arguments by value?
215212
needs_byval(@nospecialize(job::CompilerJob)) = true
216213

217-
# early processing of the newly identified LLVM kernel function
218-
function process_entry!(@nospecialize(job::CompilerJob), mod::LLVM.Module,
219-
entry::LLVM.Function)
220-
ctx = context(mod)
221-
222-
if job.config.kernel && needs_byval(job)
223-
# pass all bitstypes by value; by default Julia passes aggregates by reference
224-
# (this improves performance, and is mandated by certain back-ends like SPIR-V).
225-
args = classify_arguments(job, function_type(entry))
226-
for arg in args
227-
if arg.cc == BITS_REF
228-
attr = if LLVM.version() >= v"12"
229-
TypeAttribute("byval", eltype(arg.codegen.typ); ctx)
230-
else
231-
EnumAttribute("byval", 0; ctx)
232-
end
233-
push!(parameter_attributes(entry, arg.codegen.i), attr)
234-
end
235-
end
236-
end
237-
238-
return entry
239-
end
240-
241-
# post-Julia optimization processing of the module
242-
optimize_module!(@nospecialize(job::CompilerJob), mod::LLVM.Module) = return
243-
244-
# whether an LLVM function is valid for this back-end
245-
validate_module(@nospecialize(job::CompilerJob), mod::LLVM.Module) = IRError[]
246-
247-
# finalization of the module, before deferred codegen and optimization
248-
function finish_module!(@nospecialize(job::CompilerJob), mod::LLVM.Module, entry::LLVM.Function)
249-
return entry
250-
end
251-
252-
# final processing of the IR, right before validation and machine-code generation
253-
function finish_ir!(@nospecialize(job::CompilerJob), mod::LLVM.Module, entry::LLVM.Function)
254-
return entry
255-
end
256-
257-
add_lowering_passes!(@nospecialize(job::CompilerJob), pm::LLVM.PassManager) = return
258-
259-
link_libraries!(@nospecialize(job::CompilerJob), mod::LLVM.Module,
260-
undefined_fns::Vector{String}) = return
261-
262214
# whether pointer is a valid call target
263215
valid_function_pointer(@nospecialize(job::CompilerJob), ptr::Ptr{Cvoid}) = false
264216

@@ -305,3 +257,31 @@ function llvm_debug_info(@nospecialize(job::CompilerJob))
305257
LLVM.API.LLVMDebugEmissionKindFullDebug
306258
end
307259
end
260+
261+
262+
## extension points at important stages of compilation
263+
264+
# early extension point used to link-in external bitcode files.
265+
# this is typically used by downstream packages to link vendor libraries.
266+
link_libraries!(@nospecialize(job::CompilerJob), mod::LLVM.Module,
267+
undefined_fns::Vector{String}) = return
268+
269+
# finalization of the module, before deferred codegen and optimization
270+
finish_module!(@nospecialize(job::CompilerJob), mod::LLVM.Module, entry::LLVM.Function) =
271+
entry
272+
273+
# post-Julia optimization processing of the module
274+
optimize_module!(@nospecialize(job::CompilerJob), mod::LLVM.Module) = return
275+
276+
# final processing of the IR, right before validation and machine-code generation
277+
finish_ir!(@nospecialize(job::CompilerJob), mod::LLVM.Module, entry::LLVM.Function) =
278+
entry
279+
280+
# whether an LLVM function is valid for this back-end
281+
validate_ir(@nospecialize(job::CompilerJob), mod::LLVM.Module) = IRError[]
282+
283+
# deprecated
284+
struct DeprecationMarker end
285+
process_module!(@nospecialize(job::CompilerJob), mod::LLVM.Module) = DeprecationMarker()
286+
process_entry!(@nospecialize(job::CompilerJob), mod::LLVM.Module, entry::LLVM.Function) =
287+
DeprecationMarker()

src/irgen.jl

Lines changed: 41 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ function irgen(@nospecialize(job::CompilerJob); ctx::JuliaContextType)
77
else
88
entry_fn = compiled[job.source].func
99
end
10+
entry = functions(mod)[entry_fn]
1011

1112
# clean up incompatibilities
1213
@timeit_debug to "clean-up" begin
@@ -38,9 +39,10 @@ function irgen(@nospecialize(job::CompilerJob); ctx::JuliaContextType)
3839
end
3940
end
4041

41-
# target-specific processing
42-
process_module!(job, mod)
43-
entry = functions(mod)[entry_fn]
42+
deprecation_marker = process_module!(job, mod)
43+
if deprecation_marker != DeprecationMarker()
44+
Base.depwarn("GPUCompiler.process_module! is deprecated; implement GPUCompiler.finish_module! instead", :process_module)
45+
end
4446

4547
# sanitize function names
4648
# FIXME: Julia should do this, but apparently fails (see maleadt/LLVM.jl#201)
@@ -64,7 +66,11 @@ function irgen(@nospecialize(job::CompilerJob); ctx::JuliaContextType)
6466
elseif job.config.kernel
6567
LLVM.name!(entry, mangle_sig(job.source.specTypes))
6668
end
67-
entry = process_entry!(job, mod, entry)
69+
deprecation_marker = process_entry!(job, mod, entry)
70+
if deprecation_marker != DeprecationMarker()
71+
Base.depwarn("GPUCompiler.process_entry! is deprecated; implement GPUCompiler.finish_module! instead", :process_entry)
72+
entry = deprecation_marker
73+
end
6874
if job.config.entry_abi === :specfunc
6975
func = compiled[job.source].func
7076
specfunc = LLVM.name(entry)
@@ -77,27 +83,43 @@ function irgen(@nospecialize(job::CompilerJob); ctx::JuliaContextType)
7783
(; compiled[job.source].ci, func, specfunc)
7884

7985
# minimal required optimization
80-
@timeit_debug to "rewrite" @dispose pm=ModulePassManager() begin
81-
global current_job
82-
current_job = job
86+
@timeit_debug to "rewrite" begin
87+
if job.config.kernel && needs_byval(job)
88+
# pass all bitstypes by value; by default Julia passes aggregates by reference
89+
# (this improves performance, and is mandated by certain back-ends like SPIR-V).
90+
args = classify_arguments(job, function_type(entry))
91+
for arg in args
92+
if arg.cc == BITS_REF
93+
attr = if LLVM.version() >= v"12"
94+
TypeAttribute("byval", eltype(arg.codegen.typ); ctx=unwrap_context(ctx))
95+
else
96+
EnumAttribute("byval", 0; ctx=unwrap_context(ctx))
97+
end
98+
push!(parameter_attributes(entry, arg.codegen.i), attr)
99+
end
100+
end
101+
end
83102

84-
linkage!(entry, LLVM.API.LLVMExternalLinkage)
103+
@dispose pm=ModulePassManager() begin
104+
global current_job
105+
current_job = job
85106

86-
# internalize all functions, but keep exported global variables
87-
exports = String[LLVM.name(entry)]
88-
for gvar in globals(mod)
89-
push!(exports, LLVM.name(gvar))
90-
end
91-
internalize!(pm, exports)
107+
linkage!(entry, LLVM.API.LLVMExternalLinkage)
92108

93-
# inline llvmcall bodies
94-
always_inliner!(pm)
109+
# internalize all functions, but keep exported global variables
110+
exports = String[LLVM.name(entry)]
111+
for gvar in globals(mod)
112+
push!(exports, LLVM.name(gvar))
113+
end
114+
internalize!(pm, exports)
95115

96-
can_throw(job) || add!(pm, ModulePass("LowerThrow", lower_throw!))
116+
# inline llvmcall bodies
117+
always_inliner!(pm)
97118

98-
add_lowering_passes!(job, pm)
119+
can_throw(job) || add!(pm, ModulePass("LowerThrow", lower_throw!))
99120

100-
run!(pm, mod)
121+
run!(pm, mod)
122+
end
101123
end
102124

103125
return mod, compiled

src/metal.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,10 +88,16 @@ function finish_module!(@nospecialize(job::CompilerJob{MetalCompilerTarget}), mo
8888
# add metadata to AIR intrinsics LLVM doesn't know about
8989
annotate_air_intrinsics!(job, mod)
9090

91+
@dispose pm=ModulePassManager() begin
92+
# we emit properties (of the device and ptx isa) as private global constants,
93+
# so run the optimizer so that they are inlined before the rest of the optimizer runs.
94+
global_optimizer!(pm)
95+
end
96+
9197
return functions(mod)[entry_fn]
9298
end
9399

94-
function validate_module(job::CompilerJob{MetalCompilerTarget}, mod::LLVM.Module)
100+
function validate_ir(job::CompilerJob{MetalCompilerTarget}, mod::LLVM.Module)
95101
# Metal never supports double precision
96102
check_ir_values(mod, LLVM.DoubleType(context(mod)))
97103
end

src/native.jl

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,14 @@ function llvm_machine(target::NativeCompilerTarget)
2323
return tm
2424
end
2525

26-
function process_entry!(job::CompilerJob{NativeCompilerTarget}, mod::LLVM.Module, entry::LLVM.Function)
26+
function finish_module!(job::CompilerJob{NativeCompilerTarget}, mod::LLVM.Module, entry::LLVM.Function)
2727
ctx = context(mod)
28+
2829
if job.config.target.llvm_always_inline
2930
push!(function_attributes(entry), EnumAttribute("alwaysinline", 0; ctx))
3031
end
31-
invoke(process_entry!, Tuple{CompilerJob, LLVM.Module, LLVM.Function}, job, mod, entry)
32+
33+
return entry
3234
end
3335

3436
## job

src/ptx.jl

Lines changed: 28 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -96,17 +96,10 @@ runtime_slug(@nospecialize(job::CompilerJob{PTXCompilerTarget})) =
9696
"-debuginfo=$(Int(llvm_debug_info(job)))" *
9797
"-exitable=$(job.config.target.exitable)"
9898

99-
function process_module!(@nospecialize(job::CompilerJob{PTXCompilerTarget}), mod::LLVM.Module)
99+
function finish_module!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
100+
mod::LLVM.Module, entry::LLVM.Function)
100101
ctx = context(mod)
101102

102-
# calling convention
103-
if LLVM.version() >= v"8"
104-
for f in functions(mod)
105-
# JuliaGPU/GPUCompiler.jl#97
106-
#callconv!(f, LLVM.API.LLVMPTXDeviceCallConv)
107-
end
108-
end
109-
110103
# emit the device capability and ptx isa version as constants in the module. this makes
111104
# it possible to 'query' these in device code, relying on LLVM to optimize the checks
112105
# away and generate static code. note that we only do so if there's actual uses of these
@@ -122,35 +115,40 @@ function process_module!(@nospecialize(job::CompilerJob{PTXCompilerTarget}), mod
122115
linkage!(gv, LLVM.API.LLVMPrivateLinkage)
123116
end
124117
end
125-
end
126118

127-
function process_entry!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
128-
mod::LLVM.Module, entry::LLVM.Function)
129-
entry = invoke(process_entry!, Tuple{CompilerJob, LLVM.Module, LLVM.Function}, job, mod, entry)
119+
# update calling convention
120+
if LLVM.version() >= v"8"
121+
for f in functions(mod)
122+
# JuliaGPU/GPUCompiler.jl#97
123+
#callconv!(f, LLVM.API.LLVMPTXDeviceCallConv)
124+
end
125+
end
126+
if job.config.kernel && LLVM.version() >= v"8"
127+
callconv!(entry, LLVM.API.LLVMPTXKernelCallConv)
128+
end
130129

131130
if job.config.kernel
132-
if LLVM.version() >= v"8"
133-
# calling convention
134-
callconv!(entry, LLVM.API.LLVMPTXKernelCallConv)
135-
end
131+
# work around bad byval codegen (JuliaGPU/GPUCompiler.jl#92)
132+
entry = lower_byval(job, mod, entry)
136133
end
137134

138-
return entry
139-
end
135+
@dispose pm=ModulePassManager() begin
136+
# hide `unreachable` from LLVM so that it doesn't introduce divergent control flow
137+
if !job.config.target.unreachable
138+
add!(pm, FunctionPass("HideUnreachable", hide_unreachable!))
139+
end
140140

141-
function add_lowering_passes!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
142-
pm::LLVM.PassManager)
143-
# hide `unreachable` from LLVM so that it doesn't introduce divergent control flow
144-
if !job.config.target.unreachable
145-
add!(pm, FunctionPass("HideUnreachable", hide_unreachable!))
146-
end
141+
# even if we support `unreachable`, we still prefer `exit` to `trap`
142+
add!(pm, ModulePass("HideTrap", hide_trap!))
147143

148-
# even if we support `unreachable`, we still prefer `exit` to `trap`
149-
add!(pm, ModulePass("HideTrap", hide_trap!))
144+
# we emit properties (of the device and ptx isa) as private global constants,
145+
# so run the optimizer so that they are inlined before the rest of the optimizer runs.
146+
global_optimizer!(pm)
150147

151-
# we emit properties (of the device and ptx isa) as private global constants,
152-
# so run the optimizer so that they are inlined before the rest of the optimizer runs.
153-
global_optimizer!(pm)
148+
run!(pm, mod)
149+
end
150+
151+
return entry
154152
end
155153

156154
function optimize_module!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
@@ -187,23 +185,9 @@ function optimize_module!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
187185
end
188186
end
189187

190-
function finish_module!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
191-
mod::LLVM.Module, entry::LLVM.Function)
192-
ctx = context(mod)
193-
entry = invoke(finish_module!, Tuple{CompilerJob, LLVM.Module, LLVM.Function}, job, mod, entry)
194-
195-
if job.config.kernel
196-
# work around bad byval codegen (JuliaGPU/GPUCompiler.jl#92)
197-
entry = lower_byval(job, mod, entry)
198-
end
199-
200-
return entry
201-
end
202-
203188
function finish_ir!(@nospecialize(job::CompilerJob{PTXCompilerTarget}),
204189
mod::LLVM.Module, entry::LLVM.Function)
205190
ctx = context(mod)
206-
entry = invoke(finish_ir!, Tuple{CompilerJob, LLVM.Module, LLVM.Function}, job, mod, entry)
207191

208192
if job.config.kernel
209193
# add metadata annotations for the assembler to the module

0 commit comments

Comments
 (0)