@@ -102,6 +102,55 @@ struct InvalidIRError <: Exception
102
102
errors:: Vector{IRError}
103
103
end
104
104
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
+
105
154
const RUNTIME_FUNCTION = " call to the Julia runtime"
106
155
const UNKNOWN_FUNCTION = " call to an unknown function"
107
156
const POINTER_FUNCTION = " call through a literal pointer"
@@ -117,6 +166,8 @@ function Base.showerror(io::IO, err::InvalidIRError)
117
166
print (io, " (call to " , meta, " )" )
118
167
elseif kind == DELAYED_BINDING
119
168
print (io, " (use of '" , meta, " ')" )
169
+ else
170
+ print (io, " (" , meta, " )" )
120
171
end
121
172
end
122
173
Base. show_backtrace (io, bt)
@@ -132,8 +183,8 @@ function Base.showerror(io::IO, err::InvalidIRError)
132
183
return
133
184
end
134
185
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... )
137
188
unique! (errors)
138
189
if ! isempty (errors)
139
190
throw (InvalidIRError (job, errors))
@@ -142,18 +193,18 @@ function check_ir(job, args...)
142
193
return
143
194
end
144
195
145
- function check_ir ! (job, errors:: Vector{IRError} , mod:: LLVM.Module )
196
+ function check_llvm_ir ! (job, errors:: Vector{IRError} , mod:: LLVM.Module )
146
197
for f in functions (mod)
147
- check_ir ! (job, errors, f)
198
+ check_llvm_ir ! (job, errors, f)
148
199
end
149
200
150
201
return errors
151
202
end
152
203
153
- function check_ir ! (job, errors:: Vector{IRError} , f:: LLVM.Function )
204
+ function check_llvm_ir ! (job, errors:: Vector{IRError} , f:: LLVM.Function )
154
205
for bb in blocks (f), inst in instructions (bb)
155
206
if isa (inst, LLVM. CallInst)
156
- check_ir ! (job, errors, inst)
207
+ check_llvm_ir ! (job, errors, inst)
157
208
end
158
209
end
159
210
162
213
163
214
const libjulia = Ref {Ptr{Cvoid}} (C_NULL )
164
215
165
- function check_ir ! (job, errors:: Vector{IRError} , inst:: LLVM.CallInst )
216
+ function check_llvm_ir ! (job, errors:: Vector{IRError} , inst:: LLVM.CallInst )
166
217
bt = backtrace (inst)
167
218
dest = called_value (inst)
168
219
if isa (dest, LLVM. Function)
0 commit comments