Skip to content

Commit 53b8f95

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

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

src/jlgen.jl

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -221,34 +221,53 @@ function InferenceState(result::InferenceResult, cached::Symbol, interp::GPUInte
221221
src === nothing && return nothing
222222
validate_code_in_debug_mode(result.linfo, src, "lowered")
223223
validate_globalrefs(interp, result.linfo, src)
224+
validate_code_in_debug_mode(result.linfo, src, "validated")
225+
errors = Core.Compiler.validate_code(result.linfo, src)
226+
@safe_info "errors" errors src.code
224227
return InferenceState(result, src, cached, interp)
225228
end
226229

230+
const UNDEFINED_GLOBAL = "use of an undefined global binding"
231+
const MUTABLE_GLOBAL = "use of a mutable global binding"
232+
227233
function validate_globalrefs(interp, mi, src)
228-
function validate(x)
234+
# pseudo (single-frame) backtrace pointing to a source code location
235+
function backtrace(i)
236+
loc = src.linetable[i]
237+
[StackTraces.StackFrame(loc.method, loc.file, loc.line, mi, false, false, C_NULL)]
238+
end
239+
240+
function validate(i, x, errors::Vector{IRError})
229241
if x isa Expr
230-
return Expr(x.head, validate.(x.args))
242+
for y in x.args
243+
validate(i, y, errors)
244+
end
231245
elseif x isa GlobalRef
232246
Base.isbindingresolved(x.mod, x.name) || return
233247
# XXX: when does this happen? do we miss any cases by bailing out early?
234248
# why doesn't calling `Base.resolve(x, force=true)` work?
235249
if !Base.isdefined(x.mod, x.name)
236-
throw(KernelError(interp.job, "using undefined global: $(x.mod).$(x.name)"))
250+
push!(errors, (UNDEFINED_GLOBAL, backtrace(i), x))
237251
end
238252
if !Base.isconst(x.mod, x.name)
239-
throw(KernelError(interp.job, "using mutable global: $(x.mod).$(x.name)"))
253+
push!(errors, (MUTABLE_GLOBAL, backtrace(i), x))
240254
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.
255+
256+
# TODO: make the validation conditional, but make sure we don't cache invalid IR
244257

245258
# 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).
248259
end
260+
261+
return
249262
end
250263

251-
validate.(src.code)
264+
errors = IRError[]
265+
for (i, x) in enumerate(src.code)
266+
validate(i, x, errors)
267+
end
268+
if !isempty(errors)
269+
throw(InvalidIRError(interp.job, errors))
270+
end
252271

253272
return
254273
end

src/validation.jl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ end
9595

9696
## IR validation
9797

98+
# TODO: generalize for Julia and LLVM IR
99+
98100
const IRError = Tuple{String, StackTraces.StackTrace, Any} # kind, bt, meta
99101

100102
struct InvalidIRError <: Exception
@@ -117,6 +119,8 @@ function Base.showerror(io::IO, err::InvalidIRError)
117119
print(io, " (call to ", meta, ")")
118120
elseif kind == DELAYED_BINDING
119121
print(io, " (use of '", meta, "')")
122+
else
123+
print(io, " (", meta, ")")
120124
end
121125
end
122126
Base.show_backtrace(io, bt)

0 commit comments

Comments
 (0)