Skip to content

Commit 2fed7c3

Browse files
authored
further parametrize IRShow (#40459)
Ref JuliaDebug/Cthulhu.jl#149. This allows Ctulhu to further customize how line annotations are printed. Also moves a hook to skip printing some statements into `show_ir`, which previously Cthulhu had to duplicate. Since Cthulhu seems to be the only downstream consumer of `show_ir` https://juliahub.com/ui/RepoSearch?q=show_ir&r=true, I don't expect this change to be major. I did also try to keep compatibility for `show_ir_stmt`, which seems to be more commonly used.
1 parent 7112c89 commit 2fed7c3

5 files changed

Lines changed: 81 additions & 46 deletions

File tree

base/compiler/ssair/show.jl

Lines changed: 69 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,25 @@ function DILineInfoPrinter(linetable::Vector, showtypes::Bool=false)
487487
return emit_lineinfo_update
488488
end
489489

490+
# line_info_preprinter(io::IO, indent::String, idx::Int) may print relevant info
491+
# at the beginning of the line, and should at least print `indent`. It returns a
492+
# string that will be printed after the final basic-block annotation.
493+
# line_info_postprinter(io::IO, typ, used::Bool) prints the type-annotation at the end
494+
# of the statement
495+
# should_print_stmt(idx::Int) -> Bool: whether the statement at index `idx` should be
496+
# printed as part of the IR or not
497+
# bb_color: color used for printing the basic block brackets on the left
498+
struct IRShowConfig
499+
line_info_preprinter
500+
line_info_postprinter
501+
should_print_stmt
502+
bb_color::Symbol
503+
function IRShowConfig(line_info_preprinter, line_info_postprinter=default_expr_type_printer;
504+
should_print_stmt=Returns(true), bb_color::Symbol=:light_black)
505+
return new(line_info_preprinter, line_info_postprinter, should_print_stmt, bb_color)
506+
end
507+
end
508+
490509
struct _UNDEF
491510
global const UNDEF = _UNDEF.instance
492511
end
@@ -510,7 +529,7 @@ function _type(code::CodeInfo, idx::Int)
510529
return isassigned(types, idx) ? types[idx] : UNDEF
511530
end
512531

513-
function statement_indices_to_labels(@nospecialize(stmt), cfg::CFG)
532+
function statement_indices_to_labels(stmt, cfg::CFG)
514533
# convert statement index to labels, as expected by print_stmt
515534
if stmt isa Expr
516535
if stmt.head === :enter && length(stmt.args) == 1 && stmt.args[1] isa Int
@@ -529,16 +548,17 @@ end
529548

530549
# Show a single statement, code.stmts[idx]/code.code[idx], in the context of the whole IRCode/CodeInfo.
531550
# Returns the updated value of bb_idx.
532-
# line_info_preprinter(io::IO, indent::String, idx::Int) may print relevant info
533-
# at the beginning of the line, and should at least print `indent`. It returns a
534-
# string that will be printed after the final basic-block annotation.
535-
# line_info_postprinter(io::IO, typ, used::Bool) prints the type-annotation at the end
536-
# of the statement
537551
# pop_new_node!(idx::Int) -> (node_idx, new_node_inst, new_node_type) may return a new
538552
# node at the current index `idx`, which is printed before the statement at index
539553
# `idx`. This function is repeatedly called until it returns `nothing`
554+
function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo}, idx::Int, config::IRShowConfig,
555+
used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing))
556+
return show_ir_stmt(io, code, idx, config.line_info_preprinter, config.line_info_postprinter,
557+
used, cfg, bb_idx; pop_new_node!, config.bb_color)
558+
end
559+
540560
function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo}, idx::Int, line_info_preprinter, line_info_postprinter,
541-
used::BitSet, cfg::CFG, bb_idx::Int, pop_new_node! = _ -> nothing; bb_color=:light_black)
561+
used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), bb_color=:light_black)
542562
stmt = _stmt(code, idx)
543563
type = _type(code, idx)
544564
max_bb_idx_size = length(string(length(cfg.blocks)))
@@ -653,8 +673,8 @@ function ircode_new_nodes_iter(code::IRCode)
653673
end
654674
end
655675

656-
# corresponds to `verbose_linetable=false`
657-
function ircode_default_linfo_printer(code::IRCode)
676+
# print only line numbers on the left, some of the method names and nesting depth on the right
677+
function inline_linfo_printer(code::IRCode)
658678
loc_annotations, loc_methods, loc_lineno = compute_ir_line_annotations(code)
659679
max_loc_width = maximum(length, loc_annotations)
660680
max_lineno_width = maximum(length, loc_lineno)
@@ -693,10 +713,11 @@ end
693713
_strip_color(s::String) = replace(s, r"\e\[\d+m" => "")
694714

695715
# corresponds to `verbose_linetable=true`
696-
function ircode_verbose_linfo_printer(code::IRCode, used::BitSet)
716+
function ircode_verbose_linfo_printer(code::IRCode)
697717
stmts = code.stmts
698718
max_depth = maximum(compute_inlining_depth(code.linetable, stmts[i][:line]) for i in 1:length(stmts.line))
699719
last_stack = Ref(Int[])
720+
used = stmts_used(code, false)
700721
maxlength_idx = if isempty(used)
701722
0
702723
else
@@ -733,16 +754,27 @@ function ircode_verbose_linfo_printer(code::IRCode, used::BitSet)
733754
end
734755
end
735756

736-
function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_printer; verbose_linetable=false)
757+
function statementidx_lineinfo_printer(f, code::IRCode)
758+
printer = f(code.linetable)
759+
function (io::IO, indent::String, idx::Int)
760+
printer(io, indent, idx > 0 ? code.stmts[idx][:line] : typemin(Int32))
761+
end
762+
end
763+
function statementidx_lineinfo_printer(f, code::CodeInfo)
764+
printer = f(code.linetable)
765+
function (io::IO, indent::String, idx::Int)
766+
printer(io, indent, idx > 0 ? code.codelocs[idx] : typemin(Int32))
767+
end
768+
end
769+
statementidx_lineinfo_printer(code) = statementidx_lineinfo_printer(DILineInfoPrinter, code)
770+
771+
function stmts_used(code::IRCode, warn_unset_entry=true)
737772
stmts = code.stmts
738-
isempty(stmts) && return # unlikely, but avoid errors from reducing over empty sets
739773
used = BitSet()
740-
cfg = code.cfg
741774
for stmt in stmts
742775
scan_ssa_use!(push!, used, stmt[:inst])
743776
end
744777
new_nodes = code.new_nodes.stmts
745-
warn_unset_entry = true
746778
for nn in 1:length(new_nodes)
747779
if isassigned(new_nodes.inst, nn)
748780
scan_ssa_use!(push!, used, new_nodes[nn][:inst])
@@ -751,44 +783,42 @@ function show_ir(io::IO, code::IRCode, expr_type_printer=default_expr_type_print
751783
warn_unset_entry = false
752784
end
753785
end
754-
bb_idx = 1
755-
756-
pop_new_node! = ircode_new_nodes_iter(code)
757-
758-
if verbose_linetable
759-
line_info_preprinter = ircode_verbose_linfo_printer(code, used)
760-
else
761-
line_info_preprinter = ircode_default_linfo_printer(code)
762-
end
763-
764-
for idx in 1:length(stmts)
765-
bb_idx = show_ir_stmt(io, code, idx, line_info_preprinter, expr_type_printer,
766-
used, cfg, bb_idx, pop_new_node!; bb_color=:normal)
767-
end
768-
nothing
769-
end
770-
771-
function statementidx_lineinfo_printer(f, code::CodeInfo)
772-
printer = f(code.linetable)
773-
return (io::IO, indent::String, idx::Int) -> printer(io, indent, idx > 0 ? code.codelocs[idx] : typemin(Int32))
786+
return used
774787
end
775-
statementidx_lineinfo_printer(code::CodeInfo) = statementidx_lineinfo_printer(DILineInfoPrinter, code)
776788

777-
function show_ir(io::IO, code::CodeInfo, line_info_preprinter=statementidx_lineinfo_printer(code), line_info_postprinter=default_expr_type_printer)
789+
function stmts_used(code::CodeInfo)
778790
stmts = code.code
779791
used = BitSet()
780-
cfg = compute_basic_blocks(stmts)
781792
for stmt in stmts
782793
scan_ssa_use!(push!, used, stmt)
783794
end
795+
return used
796+
end
797+
798+
function default_config(code::IRCode; verbose_linetable=false)
799+
return IRShowConfig(verbose_linetable ? ircode_verbose_linfo_printer(code)
800+
: inline_linfo_printer(code);
801+
bb_color=:normal)
802+
end
803+
default_config(code::CodeInfo) = IRShowConfig(statementidx_lineinfo_printer(code))
804+
805+
function show_ir(io::IO, code::Union{IRCode, CodeInfo}, config::IRShowConfig=default_config(code);
806+
pop_new_node! = code isa IRCode ? ircode_new_nodes_iter(code) : Returns(nothing))
807+
stmts = code isa IRCode ? code.stmts : code.code
808+
used = stmts_used(code)
809+
cfg = code isa IRCode ? code.cfg : compute_basic_blocks(stmts)
784810
bb_idx = 1
785811

786812
for idx in 1:length(stmts)
787-
bb_idx = show_ir_stmt(io, code, idx, line_info_preprinter, line_info_postprinter, used, cfg, bb_idx)
813+
if config.should_print_stmt(code, idx, used)
814+
bb_idx = show_ir_stmt(io, code, idx, config, used, cfg, bb_idx; pop_new_node!)
815+
elseif bb_idx <= length(cfg.blocks) && idx == cfg.blocks[bb_idx].stmts.stop
816+
bb_idx += 1
817+
end
788818
end
789819

790820
max_bb_idx_size = length(string(length(cfg.blocks)))
791-
line_info_preprinter(io, " "^(max_bb_idx_size + 2), 0)
821+
config.line_info_preprinter(io, " "^(max_bb_idx_size + 2), 0)
792822
nothing
793823
end
794824

base/reflection.jl

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1262,7 +1262,11 @@ function print_statement_costs(io::IO, @nospecialize(tt::Type);
12621262
maxcost = Core.Compiler.statement_costs!(cst, code.code, code, Any[match.sparams...], false, params)
12631263
nd = ndigits(maxcost)
12641264
println(io, meth)
1265-
IRShow.show_ir(io, code, (io, linestart, idx) -> (print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " "); return ""))
1265+
irshow_config = IRShow.IRShowConfig() do io, linestart, idx
1266+
print(io, idx > 0 ? lpad(cst[idx], nd+1) : " "^(nd+1), " ")
1267+
return ""
1268+
end
1269+
IRShow.show_ir(io, code, irshow_config)
12661270
println(io)
12671271
end
12681272
end

base/show.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2476,7 +2476,7 @@ function show(io::IO, src::CodeInfo; debuginfo::Symbol=:source)
24762476
# TODO: static parameter values?
24772477
# only accepts :source or :none, we can't have a fallback for default since
24782478
# that would break code_typed(, debuginfo=:source) iff IRShow.default_debuginfo[] = :none
2479-
IRShow.show_ir(lambda_io, src, IRShow.__debuginfo[debuginfo](src))
2479+
IRShow.show_ir(lambda_io, src, IRShow.IRShowConfig(IRShow.__debuginfo[debuginfo](src)))
24802480
else
24812481
# this is a CodeInfo that has not been used as a method yet, so its locations are still LineNumberNodes
24822482
body = Expr(:block)

stdlib/InteractiveUtils/src/codeview.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,8 @@ function code_warntype(io::IO, @nospecialize(f), @nospecialize(t); debuginfo::Sy
127127
print(io, "Body")
128128
warntype_type_printer(io, rettype, true)
129129
println(io)
130-
Base.IRShow.show_ir(lambda_io, src, lineprinter(src), warntype_type_printer)
130+
irshow_config = Base.IRShow.IRShowConfig(lineprinter(src), warntype_type_printer)
131+
Base.IRShow.show_ir(lambda_io, src, irshow_config)
131132
println(io)
132133
end
133134
nothing

test/show.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1931,13 +1931,13 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1]
19311931
@test repr(src) == repr_ir
19321932
end
19331933
lines1 = split(repr(ir), '\n')
1934-
@test isempty(pop!(lines1))
1934+
@test all(isspace, pop!(lines1))
19351935
Core.Compiler.insert_node!(ir, 1, Core.Compiler.NewInstruction(QuoteNode(1), Val{1}), false)
19361936
Core.Compiler.insert_node!(ir, 1, Core.Compiler.NewInstruction(QuoteNode(2), Val{2}), true)
19371937
Core.Compiler.insert_node!(ir, length(ir.stmts.inst), Core.Compiler.NewInstruction(QuoteNode(3), Val{3}), false)
19381938
Core.Compiler.insert_node!(ir, length(ir.stmts.inst), Core.Compiler.NewInstruction(QuoteNode(4), Val{4}), true)
19391939
lines2 = split(repr(ir), '\n')
1940-
@test isempty(pop!(lines2))
1940+
@test all(isspace, pop!(lines2))
19411941
@test popfirst!(lines2) == "2 1 ── $(QuoteNode(1))"
19421942
@test popfirst!(lines2) == "$(QuoteNode(2))" # TODO: this should print after the next statement
19431943
let line1 = popfirst!(lines1)
@@ -1958,7 +1958,7 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1]
19581958

19591959
# verbose linetable
19601960
io = IOBuffer()
1961-
Base.IRShow.show_ir(io, ir; verbose_linetable=true)
1961+
Base.IRShow.show_ir(io, ir, Base.IRShow.default_config(ir; verbose_linetable=true))
19621962
seekstart(io)
19631963
@test count(contains(r"my_fun28173 at a{80}:\d+"), eachline(io)) == 9
19641964
end
@@ -1970,7 +1970,7 @@ let src = code_typed(gcd, (Int, Int), debuginfo=:source)[1][1]
19701970
ir = Core.Compiler.inflate_ir(src)
19711971
push!(ir.stmts.inst, Core.Compiler.ReturnNode())
19721972
lines = split(sprint(show, ir), '\n')
1973-
@test isempty(pop!(lines))
1973+
@test all(isspace, pop!(lines))
19741974
@test pop!(lines) == " !!! ── unreachable::#UNDEF"
19751975
end
19761976

0 commit comments

Comments
 (0)