Skip to content

Commit e3d26c2

Browse files
authored
make verify_ir error messages more informative (#56452)
Currently, when `verify_ir` finds an error, the `IRCode` is printed, but it's not easy to determine which method instance generated that `IRCode`. This commit adds method instance and code location information to the error message, making it easier to identify the problematic code. E.g.: ```julia [...] 610 │ %95 = builtin Core.tuple(%48, %94)::Tuple{GMT.Gdal.IGeometry, GMT.Gdal.IGeometry} └─── return %95 ERROR: IR verification failed. Code location: ~/julia/packages/GMT/src/gdal_extensions.jl:606 Method instance: MethodInstance for GMT.Gdal.helper_2geoms(::Matrix{Float64}, ::Matrix{Float64}) Stacktrace: [1] error(::String, ::String, ::String, ::Symbol, ::String, ::Int32, ::String, ::String, ::Core.MethodInstance) @ Core.Compiler ./error.jl:53 [...] ```
1 parent df4db53 commit e3d26c2

File tree

3 files changed

+73
-50
lines changed

3 files changed

+73
-50
lines changed

base/compiler/optimize.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1033,7 +1033,7 @@ function run_passes_ipo_safe(
10331033
end
10341034
if is_asserts()
10351035
@timeit "verify 3" begin
1036-
verify_ir(ir, true, false, optimizer_lattice(sv.inlining.interp))
1036+
verify_ir(ir, true, false, optimizer_lattice(sv.inlining.interp), sv.linfo)
10371037
verify_linetable(ir.debuginfo, length(ir.stmts))
10381038
end
10391039
end

base/compiler/ssair/verify.jl

Lines changed: 71 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ function maybe_show_ir(ir::IRCode)
77
else
88
Core.show(ir)
99
end
10+
Core.println(Core.stdout)
1011
end
1112

1213
if !isdefined(@__MODULE__, Symbol("@verify_error"))
@@ -25,7 +26,8 @@ end
2526

2627
is_toplevel_expr_head(head::Symbol) = head === :global || head === :method || head === :thunk
2728
is_value_pos_expr_head(head::Symbol) = head === :static_parameter
28-
function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int, printed_use_idx::Int, print::Bool, isforeigncall::Bool, arg_idx::Int, allow_frontend_forms::Bool)
29+
function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int, use_idx::Int, printed_use_idx::Int, print::Bool, isforeigncall::Bool, arg_idx::Int,
30+
allow_frontend_forms::Bool, @nospecialize(raise_error))
2931
if isa(op, SSAValue)
3032
op.id > 0 || @verify_error "Def ($(op.id)) is invalid in final IR"
3133
if op.id > length(ir.stmts)
@@ -39,14 +41,14 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
3941
else
4042
if op.id >= use_idx
4143
@verify_error "Def ($(op.id)) does not dominate use ($(use_idx)) in same BB"
42-
error("")
44+
raise_error()
4345
end
4446
end
4547
else
4648
if !dominates(domtree, def_bb, use_bb) && !(bb_unreachable(domtree, def_bb) && bb_unreachable(domtree, use_bb))
4749
# At the moment, we allow GC preserve tokens outside the standard domination notion
4850
@verify_error "Basic Block $def_bb does not dominate block $use_bb (tried to use value %$(op.id) at %$(printed_use_idx))"
49-
error("")
51+
raise_error()
5052
end
5153
end
5254

@@ -56,12 +58,12 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
5658
# an earlier block got deleted, but for some reason we didn't figure
5759
# out yet that this entire block is dead also.
5860
@verify_error "At statement %$use_idx: Invalid use of value statement or terminator %$(op.id)"
59-
error("")
61+
raise_error()
6062
end
6163
elseif isa(op, GlobalRef)
6264
if !isdefined(op.mod, op.name) || !isconst(op.mod, op.name)
6365
@verify_error "Unbound GlobalRef not allowed in value position"
64-
error("")
66+
raise_error()
6567
end
6668
elseif isa(op, Expr)
6769
# Only Expr(:boundscheck) is allowed in value position
@@ -72,15 +74,15 @@ function check_op(ir::IRCode, domtree::DomTree, @nospecialize(op), use_bb::Int,
7274
elseif !is_value_pos_expr_head(op.head)
7375
if !allow_frontend_forms || op.head !== :opaque_closure_method
7476
@verify_error "Expr not allowed in value position"
75-
error("")
77+
raise_error()
7678
end
7779
end
7880
elseif isa(op, Union{OldSSAValue, NewSSAValue})
7981
@verify_error "At statement %$use_idx: Left over SSA marker ($op)"
80-
error("")
82+
raise_error()
8183
elseif isa(op, SlotNumber)
8284
@verify_error "Left over slot detected in converted IR"
83-
error("")
85+
raise_error()
8486
end
8587
end
8688

@@ -96,31 +98,49 @@ end
9698

9799
function verify_ir(ir::IRCode, print::Bool=true,
98100
allow_frontend_forms::Bool=false,
99-
𝕃ₒ::AbstractLattice = SimpleInferenceLattice.instance)
101+
𝕃ₒ::AbstractLattice = SimpleInferenceLattice.instance,
102+
mi::Union{Nothing,MethodInstance}=nothing)
103+
function raise_error()
104+
error_args = Any["IR verification failed."]
105+
if isdefined(Core, :Main) && isdefined(Core.Main, :Base)
106+
# ensure we use I/O that does not yield, as this gets called during compilation
107+
firstline = invokelatest(Core.Main.Base.IRShow.debuginfo_firstline, ir.debuginfo)
108+
else
109+
firstline = nothing
110+
end
111+
if firstline !== nothing
112+
file, line = firstline
113+
push!(error_args, "\n", " Code location: ", file, ":", line)
114+
end
115+
if mi !== nothing
116+
push!(error_args, "\n", " Method instance: ", mi)
117+
end
118+
error(error_args...)
119+
end
100120
# Verify CFG graph. Must be well formed to construct domtree
101121
if !(length(ir.cfg.blocks) - 1 <= length(ir.cfg.index) <= length(ir.cfg.blocks))
102122
@verify_error "CFG index length ($(length(ir.cfg.index))) does not correspond to # of blocks $(length(ir.cfg.blocks))"
103-
error("")
123+
raise_error()
104124
end
105125
if length(ir.stmts.stmt) != length(ir.stmts)
106126
@verify_error "IR stmt length is invalid $(length(ir.stmts.stmt)) / $(length(ir.stmts))"
107-
error("")
127+
raise_error()
108128
end
109129
if length(ir.stmts.type) != length(ir.stmts)
110130
@verify_error "IR type length is invalid $(length(ir.stmts.type)) / $(length(ir.stmts))"
111-
error("")
131+
raise_error()
112132
end
113133
if length(ir.stmts.info) != length(ir.stmts)
114134
@verify_error "IR info length is invalid $(length(ir.stmts.info)) / $(length(ir.stmts))"
115-
error("")
135+
raise_error()
116136
end
117137
if length(ir.stmts.line) != length(ir.stmts) * 3
118138
@verify_error "IR line length is invalid $(length(ir.stmts.line)) / $(length(ir.stmts) * 3)"
119-
error("")
139+
raise_error()
120140
end
121141
if length(ir.stmts.flag) != length(ir.stmts)
122142
@verify_error "IR flag length is invalid $(length(ir.stmts.flag)) / $(length(ir.stmts))"
123-
error("")
143+
raise_error()
124144
end
125145
# For now require compact IR
126146
# @assert isempty(ir.new_nodes)
@@ -132,43 +152,43 @@ function verify_ir(ir::IRCode, print::Bool=true,
132152
p == 0 && continue
133153
if !(1 <= p <= length(ir.cfg.blocks))
134154
@verify_error "Predecessor $p of block $idx out of bounds for IR"
135-
error("")
155+
raise_error()
136156
end
137157
c = count_int(idx, ir.cfg.blocks[p].succs)
138158
if c == 0
139159
@verify_error "Predecessor $p of block $idx not in successor list"
140-
error("")
160+
raise_error()
141161
elseif c == 2
142162
if count_int(p, block.preds) != 2
143163
@verify_error "Double edge from $p to $idx not correctly accounted"
144-
error("")
164+
raise_error()
145165
end
146166
end
147167
end
148168
for s in block.succs
149169
if !(1 <= s <= length(ir.cfg.blocks))
150170
@verify_error "Successor $s of block $idx out of bounds for IR"
151-
error("")
171+
raise_error()
152172
end
153173
if !(idx in ir.cfg.blocks[s].preds)
154174
#Base.@show ir.cfg
155175
#Base.@show ir
156176
#Base.@show ir.argtypes
157177
@verify_error "Successor $s of block $idx not in predecessor list"
158-
error("")
178+
raise_error()
159179
end
160180
end
161181
if !(1 <= first(block.stmts) <= length(ir.stmts))
162182
@verify_error "First statement of BB $idx ($(first(block.stmts))) out of bounds for IR (length=$(length(ir.stmts)))"
163-
error("")
183+
raise_error()
164184
end
165185
if !(1 <= last(block.stmts) <= length(ir.stmts))
166186
@verify_error "Last statement of BB $idx ($(last(block.stmts))) out of bounds for IR (length=$(length(ir.stmts)))"
167-
error("")
187+
raise_error()
168188
end
169189
if idx <= length(ir.cfg.index) && last(block.stmts) + 1 != ir.cfg.index[idx]
170190
@verify_error "End of BB $idx ($(last(block.stmts))) is not one less than CFG index ($(ir.cfg.index[idx]))"
171-
error("")
191+
raise_error()
172192
end
173193
end
174194
# Verify statements
@@ -177,7 +197,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
177197
if first(block.stmts) != last_end + 1
178198
#ranges = [(idx,first(bb.stmts),last(bb.stmts)) for (idx, bb) in pairs(ir.cfg.blocks)]
179199
@verify_error "First statement of BB $idx ($(first(block.stmts))) does not match end of previous ($last_end)"
180-
error("")
200+
raise_error()
181201
end
182202
last_end = last(block.stmts)
183203
terminator = ir[SSAValue(last_end)][:stmt]
@@ -186,32 +206,32 @@ function verify_ir(ir::IRCode, print::Bool=true,
186206
if isa(terminator, ReturnNode)
187207
if !isempty(block.succs)
188208
@verify_error "Block $idx ends in return or unreachable, but has successors"
189-
error("")
209+
raise_error()
190210
end
191211
elseif isa(terminator, GotoNode)
192212
if length(block.succs) != 1 || block.succs[1] != terminator.label
193213
@verify_error "Block $idx successors ($(block.succs)), does not match GotoNode terminator ($(terminator.label))"
194-
error("")
214+
raise_error()
195215
end
196216
elseif isa(terminator, GotoIfNot)
197217
if terminator.dest == idx + 1
198218
@verify_error "Block $idx terminator forms a double edge to block $(idx+1)"
199-
error("")
219+
raise_error()
200220
end
201221
if length(block.succs) != 2 || (block.succs != [terminator.dest, idx+1] && block.succs != [idx+1, terminator.dest])
202222
@verify_error "Block $idx successors ($(block.succs)), does not match GotoIfNot terminator"
203-
error("")
223+
raise_error()
204224
end
205225
elseif isa(terminator, EnterNode)
206226
@label enter_check
207227
if length(block.succs) == 1
208228
if terminator.catch_dest != 0
209229
@verify_error "Block $idx successors ($(block.succs)), does not match :enter terminator"
210-
error("")
230+
raise_error()
211231
end
212232
elseif (block.succs != Int[terminator.catch_dest, idx+1] && block.succs != Int[idx+1, terminator.catch_dest])
213233
@verify_error "Block $idx successors ($(block.succs)), does not match :enter terminator"
214-
error("")
234+
raise_error()
215235
end
216236
else
217237
if length(block.succs) != 1 || block.succs[1] != idx + 1
@@ -233,14 +253,14 @@ function verify_ir(ir::IRCode, print::Bool=true,
233253
# here, but that isn't always possible.
234254
else
235255
@verify_error "Block $idx successors ($(block.succs)), does not match fall-through terminator %$termidx ($terminator)::$stmttyp"
236-
error("")
256+
raise_error()
237257
end
238258
end
239259
end
240260
end
241261
if length(ir.stmts) != last(ir.cfg.blocks[end].stmts)
242262
@verify_error "End of last BB $(last(ir.cfg.blocks[end].stmts)) does not match last IR statement $(length(ir.stmts))"
243-
error("")
263+
raise_error()
244264
end
245265
lastbb = 0
246266
is_phinode_block = false
@@ -260,7 +280,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
260280
if isa(stmt, PhiNode)
261281
if !is_phinode_block
262282
@verify_error "φ node $idx is not at the beginning of the basic block $bb"
263-
error("")
283+
raise_error()
264284
end
265285
lastphi = idx
266286
@assert length(stmt.edges) == length(stmt.values)
@@ -271,20 +291,20 @@ function verify_ir(ir::IRCode, print::Bool=true,
271291
if edge == edge′
272292
# TODO: Move `unique` to Core.Compiler. For now we assume the predecessor list is always unique.
273293
@verify_error "Edge list φ node $idx in bb $bb not unique (double edge?)"
274-
error("")
294+
raise_error()
275295
end
276296
end
277297
if !(edge == 0 && bb == 1) && !(edge in ir.cfg.blocks[bb].preds)
278298
#Base.@show ir.argtypes
279299
#Base.@show ir
280300
@verify_error "Edge $edge of φ node $idx not in predecessor list"
281-
error("")
301+
raise_error()
282302
end
283303
edge == 0 && continue
284304
if bb_unreachable(domtree, Int(edge))
285305
# TODO: Disallow?
286306
#@verify_error "Unreachable edge from #$edge should have been cleaned up at idx $idx"
287-
#error("")
307+
#raise_error()
288308
continue
289309
end
290310
isassigned(stmt.values, i) || continue
@@ -297,10 +317,11 @@ function verify_ir(ir::IRCode, print::Bool=true,
297317
# PhiNode type was $phiT
298318
# Value type was $(ir.stmts[val.id][:type])
299319
#"""
300-
#error("")
320+
#raise_error()
301321
end
302322
end
303-
check_op(ir, domtree, val, Int(edge), last(ir.cfg.blocks[stmt.edges[i]].stmts)+1, idx, print, false, i, allow_frontend_forms)
323+
check_op(ir, domtree, val, Int(edge), last(ir.cfg.blocks[stmt.edges[i]].stmts)+1, idx, print, false, i,
324+
allow_frontend_forms, raise_error)
304325
end
305326
continue
306327
end
@@ -311,7 +332,8 @@ function verify_ir(ir::IRCode, print::Bool=true,
311332
for validate_idx in firstidx:(lastphi-1)
312333
validate_stmt = ir[SSAValue(validate_idx)][:stmt]
313334
isa(validate_stmt, PhiNode) && continue
314-
check_op(ir, domtree, validate_stmt, bb, idx, idx, print, false, 0, allow_frontend_forms)
335+
check_op(ir, domtree, validate_stmt, bb, idx, idx, print, false, 0,
336+
allow_frontend_forms, raise_error)
315337
end
316338
is_phinode_block = false
317339
end
@@ -321,29 +343,29 @@ function verify_ir(ir::IRCode, print::Bool=true,
321343
val = stmt.values[i]
322344
if !isa(val, SSAValue)
323345
@verify_error "Operand $i of PhiC node $idx must be an SSA Value."
324-
error("")
346+
raise_error()
325347
end
326348
if !isa(ir[val][:stmt], UpsilonNode)
327349
@verify_error "Operand $i of PhiC node $idx must reference an Upsilon node."
328-
error("")
350+
raise_error()
329351
end
330352
end
331353
elseif isterminator(stmt)
332354
if idx != last(ir.cfg.blocks[bb].stmts)
333355
@verify_error "Terminator $idx in bb $bb is not the last statement in the block"
334-
error("")
356+
raise_error()
335357
end
336358
if !isa(stmt, ReturnNode) && ir[SSAValue(idx)][:type] !== Any
337359
@verify_error "Explicit terminators (other than ReturnNode) must have `Any` type"
338-
error("")
360+
raise_error()
339361
end
340362
else
341363
isforeigncall = false
342364
if isa(stmt, Expr)
343365
if stmt.head === :(=)
344366
if stmt.args[1] isa SSAValue
345367
@verify_error "SSAValue as assignment LHS"
346-
error("")
368+
raise_error()
347369
end
348370
if stmt.args[2] isa GlobalRef
349371
# undefined GlobalRef as assignment RHS is OK
@@ -352,7 +374,7 @@ function verify_ir(ir::IRCode, print::Bool=true,
352374
elseif stmt.head === :isdefined
353375
if length(stmt.args) > 2 || (length(stmt.args) == 2 && !isa(stmt.args[2], Bool))
354376
@verify_error "malformed isdefined"
355-
error("")
377+
raise_error()
356378
end
357379
if stmt.args[1] isa GlobalRef
358380
# undefined GlobalRef is OK in isdefined
@@ -380,12 +402,12 @@ function verify_ir(ir::IRCode, print::Bool=true,
380402
arg = stmt.args[i]
381403
if !isa(arg, Union{Nothing, SSAValue})
382404
@verify_error "Malformed :leave - Expected `Nothing` or SSAValue"
383-
error()
405+
raise_error()
384406
elseif isa(arg, SSAValue)
385407
enter_stmt = ir[arg::SSAValue][:stmt]
386408
if !isa(enter_stmt, Nothing) && !isa(enter_stmt, EnterNode)
387409
@verify_error "Malformed :leave - argument ssavalue should point to `nothing` or :enter"
388-
error()
410+
raise_error()
389411
end
390412
end
391413
end
@@ -394,7 +416,8 @@ function verify_ir(ir::IRCode, print::Bool=true,
394416
n = 1
395417
for op in userefs(stmt)
396418
op = op[]
397-
check_op(ir, domtree, op, bb, idx, idx, print, isforeigncall, n, allow_frontend_forms)
419+
check_op(ir, domtree, op, bb, idx, idx, print, isforeigncall, n,
420+
allow_frontend_forms, raise_error)
398421
n += 1
399422
end
400423
end

test/compiler/ssair.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ let code = Any[
172172
]
173173
ir = make_ircode(code; verify=false)
174174
ir = Core.Compiler.compact!(ir, true)
175-
@test_throws ErrorException Core.Compiler.verify_ir(ir, false)
175+
@test_throws ["IR verification failed.", "Code location: "] Core.Compiler.verify_ir(ir, false)
176176
end
177177

178178
# Issue #29107

0 commit comments

Comments
 (0)