Skip to content

Commit 395aaf0

Browse files
committed
Revert "Revert "RFC: prettier IR-show for line number and inlining information" (JuliaLang#29118)"
This reverts commit 6caabc9.
1 parent 3b3a163 commit 395aaf0

File tree

7 files changed

+371
-163
lines changed

7 files changed

+371
-163
lines changed

base/compiler/ssair/ir.jl

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,11 @@ function basic_blocks_starts(stmts::Vector{Any})
8585
push!(jump_dests, idx)
8686
push!(jump_dests, idx+1)
8787
# The catch block is a jump dest
88-
push!(jump_dests, stmt.args[1])
88+
push!(jump_dests, stmt.args[1]::Int)
8989
elseif stmt.head === :gotoifnot
9090
# also tolerate expr form of IR
9191
push!(jump_dests, idx+1)
92-
push!(jump_dests, stmt.args[2])
92+
push!(jump_dests, stmt.args[2]::Int)
9393
elseif stmt.head === :return
9494
# also tolerate expr form of IR
9595
# This is a fake dest to force the next stmt to start a bb
@@ -130,7 +130,7 @@ function compute_basic_blocks(stmts::Vector{Any})
130130
# Compute successors/predecessors
131131
for (num, b) in enumerate(blocks)
132132
terminator = stmts[last(b.stmts)]
133-
if isa(terminator, ReturnNode)
133+
if isa(terminator, ReturnNode) || isexpr(terminator, :return)
134134
# return never has any successors
135135
continue
136136
end
@@ -150,17 +150,28 @@ function compute_basic_blocks(stmts::Vector{Any})
150150
push!(blocks[block′].preds, num)
151151
push!(b.succs, block′)
152152
end
153-
elseif isa(terminator, Expr) && terminator.head == :enter
154-
# :enter gets a virtual edge to the exception handler and
155-
# the exception handler gets a virtual edge from outside
156-
# the function.
157-
# See the devdocs on exception handling in SSA form (or
158-
# bug Keno to write them, if you're reading this and they
159-
# don't exist)
160-
block′ = block_for_inst(basic_block_index, terminator.args[1])
161-
push!(blocks[block′].preds, num)
162-
push!(blocks[block′].preds, 0)
163-
push!(b.succs, block′)
153+
elseif isa(terminator, Expr)
154+
if terminator.head == :enter
155+
# :enter gets a virtual edge to the exception handler and
156+
# the exception handler gets a virtual edge from outside
157+
# the function.
158+
# See the devdocs on exception handling in SSA form (or
159+
# bug Keno to write them, if you're reading this and they
160+
# don't exist)
161+
block′ = block_for_inst(basic_block_index, terminator.args[1]::Int)
162+
push!(blocks[block′].preds, num)
163+
push!(blocks[block′].preds, 0)
164+
push!(b.succs, block′)
165+
elseif terminator.head == :gotoifnot
166+
block′ = block_for_inst(basic_block_index, terminator.args[2]::Int)
167+
if block′ == num + 1
168+
# This GotoIfNot acts like a noop - treat it as such.
169+
# We will drop it during SSA renaming
170+
else
171+
push!(blocks[block′].preds, num)
172+
push!(b.succs, block′)
173+
end
174+
end
164175
end
165176
# statement fall-through
166177
if num + 1 <= length(blocks)

base/compiler/ssair/show.jl

Lines changed: 160 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,15 @@ function Base.show(io::IO, cfg::CFG)
2121
end
2222

2323
function print_stmt(io::IO, idx::Int, @nospecialize(stmt), used::BitSet, maxlength_idx::Int, color::Bool, show_type::Bool)
24-
indent = maxlength_idx + 4
2524
if idx in used
26-
pad = " "^(maxlength_idx - length(string(idx)) + 1)
27-
print(io, "%", idx, pad, "= ")
25+
idx_s = string(idx)
26+
pad = " "^(maxlength_idx - length(idx_s) + 1)
27+
print(io, "%", idx_s, pad, "= ")
2828
else
29-
print(io, " "^indent)
29+
print(io, " "^(maxlength_idx + 4))
3030
end
31+
# TODO: `indent` is supposed to be the full width of the leader for correct alignment
32+
indent = 16
3133
if !color && stmt isa PiNode
3234
# when the outer context is already colored (yellow, for pending nodes), don't use the usual coloring printer
3335
print(io, "π (")
@@ -314,6 +316,145 @@ end
314316

315317
Base.show(io::IO, code::IRCode) = show_ir(io, code)
316318

319+
320+
lineinfo_disabled(io::IO, linestart::String, lineidx::Int32) = ""
321+
322+
function DILineInfoPrinter(linetable::Vector)
323+
context = LineInfoNode[]
324+
context_depth = Ref(0)
325+
indent(s::String) = s^(max(context_depth[], 1) - 1)
326+
function emit_lineinfo_update(io::IO, linestart::String, lineidx::Int32)
327+
# internal configuration options:
328+
collapse = true
329+
indent_all = true
330+
# convert lineidx to a vector
331+
lineidx == 0 && return indent_all ? indent("") : "" # just skip over lines with no debug info at all
332+
DI = LineInfoNode[]
333+
while lineidx != 0
334+
entry = linetable[lineidx]::LineInfoNode
335+
push!(DI, entry)
336+
lineidx = entry.inlined_at
337+
end
338+
nframes = length(DI)
339+
nctx = 0
340+
pop_skips = 0
341+
# compute the size of the matching prefix in the inlining information stack
342+
for i = 1:min(length(context), nframes)
343+
CtxLine = context[i]
344+
FrameLine = DI[nframes - i + 1]
345+
CtxLine === FrameLine || break
346+
nctx = i
347+
end
348+
update_line_only = false
349+
if collapse && 0 < nctx
350+
# check if we're adding more frames with the same method name,
351+
# if so, drop all existing calls to it from the top of the context
352+
# AND check if instead the context was previously printed that way
353+
# but now has removed the recursive frames
354+
let method = context[nctx].method
355+
if (nctx < nframes && DI[nframes - nctx].method === method) ||
356+
(nctx < length(context) && context[nctx + 1].method === method)
357+
update_line_only = true
358+
while nctx > 0 && context[nctx].method === method
359+
nctx -= 1
360+
end
361+
end
362+
end
363+
end
364+
# examine what frames we're returning from
365+
if nctx < length(context)
366+
# compute the new inlining depth
367+
if collapse
368+
npops = 1
369+
let Prev = context[nctx + 1].method
370+
for i = (nctx + 2):length(context)
371+
Next = context[i].method
372+
Prev === Next || (npops += 1)
373+
Prev = Next
374+
end
375+
end
376+
else
377+
npops = length(context) - nctx
378+
end
379+
# look at the first non-matching element to see if we are only changing the line number
380+
if !update_line_only && nctx < nframes
381+
let CtxLine = context[nctx + 1],
382+
FrameLine = DI[nframes - nctx]
383+
if CtxLine.file == FrameLine.file &&
384+
CtxLine.method == FrameLine.method &&
385+
CtxLine.mod == FrameLine.mod
386+
update_line_only = true
387+
end
388+
end
389+
end
390+
resize!(context, nctx)
391+
update_line_only && (npops -= 1)
392+
if npops > 0
393+
context_depth[] -= npops
394+
print(io, linestart, indent(""), ""^npops, "\n")
395+
end
396+
end
397+
# see what change we made to the outermost line number
398+
if update_line_only
399+
frame = DI[nframes - nctx]
400+
nctx += 1
401+
push!(context, frame)
402+
if frame.line != typemax(frame.line) && frame.line != 0
403+
print(io, linestart, indent(""), " @ ", frame.file, ":", frame.line, " within `", frame.method, "'")
404+
if collapse
405+
method = frame.method
406+
while nctx < nframes
407+
frame = DI[nframes - nctx]
408+
frame.method === method || break
409+
nctx += 1
410+
push!(context, frame)
411+
print(io, " @ ", frame.file, ":", frame.line)
412+
end
413+
end
414+
print(io, "\n")
415+
end
416+
end
417+
# now print the rest of the new frames
418+
while nctx < nframes
419+
frame = DI[nframes - nctx]
420+
print(io, linestart, indent(""))
421+
nctx += 1
422+
push!(context, frame)
423+
context_depth[] += 1
424+
nctx != 1 && print(io, "")
425+
print(io, " @ ", frame.file)
426+
if frame.line != typemax(frame.line) && frame.line != 0
427+
print(io, ":", frame.line)
428+
end
429+
print(io, " within `", frame.method, "'")
430+
if collapse
431+
method = frame.method
432+
while nctx < nframes
433+
frame = DI[nframes - nctx]
434+
frame.method === method || break
435+
nctx += 1
436+
push!(context, frame)
437+
print(io, " @ ", frame.file, ":", frame.line)
438+
end
439+
end
440+
print(io, "\n")
441+
end
442+
# FOR DEBUGGING `collapse`:
443+
#let Prev = context[1].method,
444+
# depth2 = 1
445+
# for i = 2:nctx
446+
# Next = context[i].method
447+
# (collapse && Prev === Next) || (depth2 += 1)
448+
# Prev = Next
449+
# end
450+
# @assert context_depth[] == depth2
451+
#end
452+
return indent_all ? indent("") : ""
453+
end
454+
return emit_lineinfo_update
455+
end
456+
457+
317458
function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_printer; verbose_linetable=false)
318459
cols = displaysize(io)[2]
319460
used = BitSet()
@@ -465,7 +606,7 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print
465606
end
466607
end
467608

468-
function show_ir(io::IO, code::CodeInfo, expr_type_printer=default_expr_type_printer; verbose_linetable=false)
609+
function show_ir(io::IO, code::CodeInfo, line_info_preprinter=DILineInfoPrinter(code.linetable), line_info_postprinter=default_expr_type_printer)
469610
cols = displaysize(io)[2]
470611
used = BitSet()
471612
stmts = code.code
@@ -483,14 +624,6 @@ function show_ir(io::IO, code::CodeInfo, expr_type_printer=default_expr_type_pri
483624
maxused = maximum(used)
484625
maxlength_idx = length(string(maxused))
485626
end
486-
if !verbose_linetable
487-
(loc_annotations, loc_methods, loc_lineno) = compute_ir_line_annotations(code)
488-
max_loc_width = maximum(length(str) for str in loc_annotations)
489-
max_lineno_width = maximum(length(str) for str in loc_lineno)
490-
max_method_width = maximum(length(str) for str in loc_methods)
491-
end
492-
max_depth = maximum(compute_inlining_depth(code.linetable, line) for line in code.codelocs)
493-
last_stack = []
494627
for idx in eachindex(stmts)
495628
if !isassigned(stmts, idx)
496629
# This is invalid, but do something useful rather
@@ -499,63 +632,24 @@ function show_ir(io::IO, code::CodeInfo, expr_type_printer=default_expr_type_pri
499632
continue
500633
end
501634
stmt = stmts[idx]
502-
# Compute BB guard rail
503635
bbrange = cfg.blocks[bb_idx].stmts
504636
bbrange = bbrange.first:bbrange.last
505-
bb_idx_str = string(bb_idx)
506-
bb_pad = max_bb_idx_size - length(bb_idx_str)
507-
bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "" : ""
508-
bb_start_str = string(bb_idx_str, " ", bb_type, ""^bb_pad, " ")
509-
bb_guard_rail_cont = string("", " "^max_bb_idx_size)
637+
# Print line info update
638+
linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "", color=:light_black), context=io)
639+
linestart *= " "^max_bb_idx_size
640+
inlining_indent = line_info_preprinter(io, linestart, code.codelocs[idx])
641+
# Compute BB guard rail
510642
if idx == first(bbrange)
511-
bb_guard_rail = bb_start_str
512-
else
513-
bb_guard_rail = bb_guard_rail_cont
514-
end
515-
# Print linetable information
516-
if verbose_linetable
517-
stack = compute_loc_stack(code.linetable, code.codelocs[idx])
518-
# We need to print any stack frames that did not exist in the last stack
519-
ndepth = max(1, length(stack))
520-
rail = string(" "^(max_depth+1-ndepth), ""^ndepth)
521-
start_column = cols - max_depth - 10
522-
for (i, x) in enumerate(stack)
523-
if i > length(last_stack) || last_stack[i] != x
524-
entry = code.linetable[x]
525-
printstyled(io, "\e[$(start_column)G$(rail)\e[1G", color = :light_black)
526-
print(io, bb_guard_rail)
527-
ssa_guard = " "^(maxlength_idx + 4 + (i - 1))
528-
entry_label = "$(ssa_guard)$(entry.method) at $(entry.file):$(entry.line) "
529-
hline = string(""^(start_column-length(entry_label)-length(bb_guard_rail)+max_depth-i), "")
530-
printstyled(io, string(entry_label, hline), "\n"; color=:light_black)
531-
bb_guard_rail = bb_guard_rail_cont
532-
end
533-
end
534-
printstyled(io, "\e[$(start_column)G$(rail)\e[1G", color = :light_black)
535-
last_stack = stack
643+
bb_idx_str = string(bb_idx)
644+
bb_pad = max_bb_idx_size - length(bb_idx_str)
645+
bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "" : ""
646+
printstyled(io, bb_idx_str, " ", bb_type, ""^bb_pad, color=:light_black)
647+
elseif idx == last(bbrange) # print separator
648+
printstyled(io, "", ""^(1 + max_bb_idx_size), color=:light_black)
536649
else
537-
annotation = loc_annotations[idx]
538-
loc_method = loc_methods[idx]
539-
lineno = loc_lineno[idx]
540-
# Print location information right aligned. If the line below is too long, it'll overwrite this,
541-
# but that's what we want.
542-
if get(io, :color, false)
543-
method_start_column = cols - max_method_width - max_loc_width - 2
544-
filler = " "^(max_loc_width-length(annotation))
545-
printstyled(io, "\e[$(method_start_column)G$(annotation)$(filler)$(loc_method)\e[1G", color = :light_black)
546-
end
547-
printstyled(io, lineno, " "^(max_lineno_width-length(lineno)+1); color = :light_black)
548-
end
549-
idx != last(bbrange) && print(io, bb_guard_rail)
550-
if idx == last(bbrange) # print separator
551-
if idx == first(bbrange)
552-
print(io, bb_start_str)
553-
elseif idx == last(bbrange)
554-
print(io, "", ""^(1 + max_bb_idx_size), " ")
555-
else
556-
print(io, "", " "^max_bb_idx_size)
557-
end
650+
printstyled(io, "", " "^max_bb_idx_size, color=:light_black)
558651
end
652+
print(io, inlining_indent, " ")
559653
if idx == last(bbrange)
560654
bb_idx += 1
561655
end
@@ -582,7 +676,7 @@ function show_ir(io::IO, code::CodeInfo, expr_type_printer=default_expr_type_pri
582676
printstyled(io, "::#UNDEF", color=:red)
583677
elseif show_type
584678
typ = types[idx]
585-
expr_type_printer(io, typ, idx in used)
679+
line_info_postprinter(io, typ, idx in used)
586680
end
587681
end
588682
println(io)

doc/src/devdocs/reflection.md

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -94,12 +94,13 @@ particular interest for understanding how language constructs map to primitive o
9494
as assignments, branches, and calls:
9595

9696
```jldoctest
97-
julia> Meta.lower(@__MODULE__, :([1+2, sin(0.5)]) )
97+
julia> Meta.lower(@__MODULE__, :( [1+2, sin(0.5)] ))
9898
:($(Expr(:thunk, CodeInfo(
99-
1 ─ %1 = 1 + 2
100-
│ %2 = sin(0.5)
101-
│ %3 = (Base.vect)(%1, %2)
102-
└── return %3
99+
@ none within `top-level scope'
100+
1 ─ %1 = 1 + 2
101+
│ %2 = sin(0.5)
102+
│ %3 = (Base.vect)(%1, %2)
103+
└── return %3
103104
))))
104105
```
105106

@@ -122,11 +123,11 @@ calls and expand argument types automatically:
122123
```julia-repl
123124
julia> @code_llvm +(1,1)
124125
125-
; Function Attrs: sspreq
126-
define i64 @"julia_+_130862"(i64, i64) #0 {
126+
; @ int.jl:53 within `+'
127+
define i64 @"julia_+_130862"(i64, i64) {
127128
top:
128-
%2 = add i64 %1, %0, !dbg !8
129-
ret i64 %2, !dbg !8
129+
%2 = add i64 %1, %0
130+
ret i64 %2
130131
}
131132
```
132133

0 commit comments

Comments
 (0)