Skip to content

Commit c2c8a59

Browse files
committed
Reuse existing IRError infrastructure.
1 parent c8d4aec commit c2c8a59

File tree

3 files changed

+60
-38
lines changed

3 files changed

+60
-38
lines changed

src/driver.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -406,7 +406,7 @@ end
406406
if validate
407407
@timeit_debug to "validation" begin
408408
check_invocation(job)
409-
check_ir(job, ir)
409+
check_llvm_ir(job, ir)
410410
end
411411
end
412412

src/jlgen.jl

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -220,39 +220,10 @@ function InferenceState(result::InferenceResult, cached::Symbol, interp::GPUInte
220220
src = retrieve_code_info(result.linfo)
221221
src === nothing && return nothing
222222
validate_code_in_debug_mode(result.linfo, src, "lowered")
223-
validate_globalrefs(interp, result.linfo, src)
223+
check_julia_ir(interp, result.linfo, src)
224224
return InferenceState(result, src, cached, interp)
225225
end
226226

227-
function validate_globalrefs(interp, mi, src)
228-
function validate(x)
229-
if x isa Expr
230-
return Expr(x.head, validate.(x.args))
231-
elseif x isa GlobalRef
232-
Base.isbindingresolved(x.mod, x.name) || return
233-
# XXX: when does this happen? do we miss any cases by bailing out early?
234-
# why doesn't calling `Base.resolve(x, force=true)` work?
235-
if !Base.isdefined(x.mod, x.name)
236-
throw(KernelError(interp.job, "using undefined global: $(x.mod).$(x.name)"))
237-
end
238-
if !Base.isconst(x.mod, x.name)
239-
throw(KernelError(interp.job, "using mutable global: $(x.mod).$(x.name)"))
240-
end
241-
# XXX: can we use KernelError? and make the validation conditional? both are
242-
# complicated by the fact that we don't have the CompilerJob here,
243-
# and that inference results can be cached across jobs.
244-
245-
# TODO: perform more validation? e.g. disallow Arrays and other CPU values?
246-
# probably requires an interface, so again access to the CompilerJob
247-
# (as a CPU-back-end would still support such values).
248-
end
249-
end
250-
251-
validate.(src.code)
252-
253-
return
254-
end
255-
256227

257228
function Core.Compiler.add_remark!(interp::GPUInterpreter, sv::InferenceState, msg)
258229
@safe_debug "Inference remark during GPU compilation of $(sv.linfo): $msg"

src/validation.jl

Lines changed: 58 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,55 @@ struct InvalidIRError <: Exception
102102
errors::Vector{IRError}
103103
end
104104

105+
# Julia IR
106+
107+
const UNDEFINED_GLOBAL = "use of an undefined global binding"
108+
const MUTABLE_GLOBAL = "use of a mutable global binding"
109+
110+
function check_julia_ir(interp, mi, src)
111+
# pseudo (single-frame) backtrace pointing to a source code location
112+
function backtrace(i)
113+
loc = src.linetable[i]
114+
[StackTraces.StackFrame(loc.method, loc.file, loc.line, mi, false, false, C_NULL)]
115+
end
116+
117+
function check(i, x, errors::Vector{IRError})
118+
if x isa Expr
119+
for y in x.args
120+
check(i, y, errors)
121+
end
122+
elseif x isa GlobalRef
123+
Base.isbindingresolved(x.mod, x.name) || return
124+
# XXX: when does this happen? do we miss any cases by bailing out early?
125+
# why doesn't calling `Base.resolve(x, force=true)` work?
126+
if !Base.isdefined(x.mod, x.name)
127+
push!(errors, (UNDEFINED_GLOBAL, backtrace(i), x))
128+
end
129+
if !Base.isconst(x.mod, x.name)
130+
push!(errors, (MUTABLE_GLOBAL, backtrace(i), x))
131+
end
132+
133+
# TODO: make the validation conditional, but make sure we don't cache invalid IR
134+
135+
# TODO: perform more validation? e.g. disallow Arrays and other CPU values?
136+
end
137+
138+
return
139+
end
140+
141+
errors = IRError[]
142+
for (i, x) in enumerate(src.code)
143+
check(i, x, errors)
144+
end
145+
if !isempty(errors)
146+
throw(InvalidIRError(interp.job, errors))
147+
end
148+
149+
return
150+
end
151+
152+
# LLVM IR
153+
105154
const RUNTIME_FUNCTION = "call to the Julia runtime"
106155
const UNKNOWN_FUNCTION = "call to an unknown function"
107156
const POINTER_FUNCTION = "call through a literal pointer"
@@ -117,6 +166,8 @@ function Base.showerror(io::IO, err::InvalidIRError)
117166
print(io, " (call to ", meta, ")")
118167
elseif kind == DELAYED_BINDING
119168
print(io, " (use of '", meta, "')")
169+
else
170+
print(io, " (", meta, ")")
120171
end
121172
end
122173
Base.show_backtrace(io, bt)
@@ -132,8 +183,8 @@ function Base.showerror(io::IO, err::InvalidIRError)
132183
return
133184
end
134185

135-
function check_ir(job, args...)
136-
errors = check_ir!(job, IRError[], args...)
186+
function check_llvm_ir(job, args...)
187+
errors = check_llvm_ir!(job, IRError[], args...)
137188
unique!(errors)
138189
if !isempty(errors)
139190
throw(InvalidIRError(job, errors))
@@ -142,18 +193,18 @@ function check_ir(job, args...)
142193
return
143194
end
144195

145-
function check_ir!(job, errors::Vector{IRError}, mod::LLVM.Module)
196+
function check_llvm_ir!(job, errors::Vector{IRError}, mod::LLVM.Module)
146197
for f in functions(mod)
147-
check_ir!(job, errors, f)
198+
check_llvm_ir!(job, errors, f)
148199
end
149200

150201
return errors
151202
end
152203

153-
function check_ir!(job, errors::Vector{IRError}, f::LLVM.Function)
204+
function check_llvm_ir!(job, errors::Vector{IRError}, f::LLVM.Function)
154205
for bb in blocks(f), inst in instructions(bb)
155206
if isa(inst, LLVM.CallInst)
156-
check_ir!(job, errors, inst)
207+
check_llvm_ir!(job, errors, inst)
157208
end
158209
end
159210

@@ -162,7 +213,7 @@ end
162213

163214
const libjulia = Ref{Ptr{Cvoid}}(C_NULL)
164215

165-
function check_ir!(job, errors::Vector{IRError}, inst::LLVM.CallInst)
216+
function check_llvm_ir!(job, errors::Vector{IRError}, inst::LLVM.CallInst)
166217
bt = backtrace(inst)
167218
dest = called_value(inst)
168219
if isa(dest, LLVM.Function)

0 commit comments

Comments
 (0)