@@ -102,6 +102,55 @@ struct InvalidIRError <: Exception
102102 errors:: Vector{IRError}
103103end
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+
105154const RUNTIME_FUNCTION = " call to the Julia runtime"
106155const UNKNOWN_FUNCTION = " call to an unknown function"
107156const 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
133184end
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
143194end
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
151202end
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
162213
163214const 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