Skip to content

Commit 4c0f8de

Browse files
authored
Fix display of new after-attach IR nodes. (#47092)
1 parent f927d25 commit 4c0f8de

File tree

2 files changed

+148
-51
lines changed

2 files changed

+148
-51
lines changed

base/compiler/ssair/show.jl

Lines changed: 88 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -579,17 +579,18 @@ end
579579

580580
# Show a single statement, code.stmts[idx]/code.code[idx], in the context of the whole IRCode/CodeInfo.
581581
# Returns the updated value of bb_idx.
582-
# pop_new_node!(idx::Int) -> (node_idx, new_node_inst, new_node_type) may return a new
583-
# node at the current index `idx`, which is printed before the statement at index
584-
# `idx`. This function is repeatedly called until it returns `nothing`
582+
# pop_new_node!(idx::Int; attach_after=false) -> (node_idx, new_node_inst, new_node_type)
583+
# may return a new node at the current index `idx`, which is printed before the statement
584+
# at index `idx`. This function is repeatedly called until it returns `nothing`.
585+
# to iterate nodes that are to be inserted after the statement, set `attach_after=true`.
585586
function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, config::IRShowConfig,
586-
used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing))
587+
used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false)
587588
return show_ir_stmt(io, code, idx, config.line_info_preprinter, config.line_info_postprinter,
588-
used, cfg, bb_idx; pop_new_node!, config.bb_color)
589+
used, cfg, bb_idx; pop_new_node!, only_after, config.bb_color)
589590
end
590591

591592
function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, line_info_preprinter, line_info_postprinter,
592-
used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), bb_color=:light_black)
593+
used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false, bb_color=:light_black)
593594
stmt = _stmt(code, idx)
594595
type = _type(code, idx)
595596
max_bb_idx_size = length(string(length(cfg.blocks)))
@@ -609,8 +610,7 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
609610
end
610611

611612
i = 1
612-
while true
613-
next = pop_new_node!(idx)
613+
function print_indentation(final::Bool=true)
614614
# Compute BB guard rail
615615
if bb_idx > length(cfg.blocks)
616616
# If invariants are violated, print a special leader
@@ -619,7 +619,6 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
619619
printstyled(io, "!!! ", ""^max_bb_idx_size, color=bb_color)
620620
else
621621
bbrange = cfg.blocks[bb_idx].stmts
622-
bbrange = bbrange.start:bbrange.stop
623622
# Print line info update
624623
linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "", color=bb_color), context=io)
625624
linestart *= " "^max_bb_idx_size
@@ -632,24 +631,20 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
632631
bb_pad = max_bb_idx_size - length(bb_idx_str)
633632
bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "" : ""
634633
printstyled(io, bb_idx_str, " ", bb_type, ""^bb_pad, color=bb_color)
635-
elseif next === nothing && idx == last(bbrange) # print separator
634+
elseif final && idx == last(bbrange) # print separator
636635
printstyled(io, "", ""^(1 + max_bb_idx_size), color=bb_color)
637636
else
638637
printstyled(io, "", " "^max_bb_idx_size, color=bb_color)
639638
end
640639
end
641640
print(io, inlining_indent, " ")
641+
end
642642

643-
if next === nothing
644-
if bb_idx <= length(cfg.blocks) && idx == last(bbrange)
645-
bb_idx += 1
646-
end
647-
break
648-
end
649-
650-
# print new nodes first in the right position
651-
node_idx, new_node_inst, new_node_type = next
643+
# first, print new nodes that are to be inserted before the current statement
644+
function print_new_node(node; final::Bool=true)
645+
print_indentation(final)
652646

647+
node_idx, new_node_inst, new_node_type = node
653648
@assert new_node_inst !== UNDEF # we filtered these out earlier
654649
show_type = should_print_ssa_type(new_node_inst)
655650
let maxlength_idx=maxlength_idx, show_type=show_type
@@ -664,43 +659,84 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
664659
line_info_postprinter(io; type = new_node_type, used = node_idx in used, show_type, idx = node_idx)
665660
end
666661
println(io)
662+
end
663+
while (next = pop_new_node!(idx)) !== nothing
664+
only_after || print_new_node(next; final=false)
667665
i += 1
668666
end
669-
if code isa CodeInfo
670-
stmt = statement_indices_to_labels(stmt, cfg)
667+
668+
# peek at the nodes to be inserted after the current statement
669+
# (to determine of the statement itself is the final one)
670+
next = pop_new_node!(idx; attach_after=true)
671+
672+
# then, print the current statement
673+
# FIXME: `only_after` is hack so that we can call this function to print uncompacted
674+
# attach-after nodes when the current node has already been compated already
675+
if !only_after
676+
print_indentation(next===nothing)
677+
if code isa CodeInfo
678+
stmt = statement_indices_to_labels(stmt, cfg)
679+
end
680+
show_type = type !== nothing && should_print_ssa_type(stmt)
681+
print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type)
682+
if type !== nothing # ignore types for pre-inference code
683+
if type === UNDEF
684+
# This is an error, but can happen if passes don't update their type information
685+
printstyled(io, "::#UNDEF", color=:red)
686+
else
687+
line_info_postprinter(io; type, used = idx in used, show_type, idx)
688+
end
689+
end
690+
println(io)
671691
end
672-
show_type = type !== nothing && should_print_ssa_type(stmt)
673-
print_stmt(io, idx, stmt, used, maxlength_idx, true, show_type)
674-
if type !== nothing # ignore types for pre-inference code
675-
if type === UNDEF
676-
# This is an error, but can happen if passes don't update their type information
677-
printstyled(io, "::#UNDEF", color=:red)
678-
else
679-
line_info_postprinter(io; type, used = idx in used, show_type, idx)
692+
i += 1
693+
694+
# finally, print new nodes that are to be inserted after the current statement
695+
while next !== nothing
696+
print_new_node(next)
697+
i += 1
698+
next = pop_new_node!(idx; attach_after=true)
699+
end
700+
701+
# increment the basic block counter
702+
if bb_idx <= length(cfg.blocks)
703+
bbrange = cfg.blocks[bb_idx].stmts
704+
if bb_idx <= length(cfg.blocks) && idx == last(bbrange)
705+
bb_idx += 1
680706
end
681707
end
682-
println(io)
708+
683709
return bb_idx
684710
end
685711

686712
function _new_nodes_iter(stmts, new_nodes, new_nodes_info, new_nodes_idx)
687713
new_nodes_perm = filter(i -> isassigned(new_nodes.inst, i), 1:length(new_nodes))
688714
sort!(new_nodes_perm, by = x -> (x = new_nodes_info[x]; (x.pos, x.attach_after)))
689-
perm_idx = Ref(1)
690-
691-
return function get_new_node(idx::Int)
692-
perm_idx[] <= length(new_nodes_perm) || return nothing
693-
node_idx = new_nodes_perm[perm_idx[]]
694-
if node_idx < new_nodes_idx
695-
# skip new nodes that have already been processed by incremental compact
696-
# (but don't just return nothing because there may be multiple at this pos)
697-
perm_idx[] += 1
698-
return get_new_node(idx)
715+
716+
# separate iterators for the nodes that are inserted before resp. after each statement
717+
before_iter = Ref(1)
718+
after_iter = Ref(1)
719+
720+
return function get_new_node(idx::Int; attach_after=false)
721+
iter = attach_after ? after_iter : before_iter
722+
iter[] <= length(new_nodes_perm) || return nothing
723+
node_idx = new_nodes_perm[iter[]]
724+
725+
# skip nodes
726+
while node_idx < new_nodes_idx || # already compacted
727+
idx > new_nodes_info[node_idx].pos || # not interested in
728+
new_nodes_info[node_idx].attach_after != attach_after
729+
iter[] += 1
730+
iter[] > length(new_nodes_perm) && return nothing
731+
node_idx = new_nodes_perm[iter[]]
699732
end
700-
if new_nodes_info[node_idx].pos != idx
733+
734+
if new_nodes_info[node_idx].pos != idx ||
735+
new_nodes_info[node_idx].attach_after != attach_after
701736
return nothing
702737
end
703-
perm_idx[] += 1
738+
739+
iter[] += 1
704740
new_node = new_nodes[node_idx]
705741
new_node_inst = isassigned(new_nodes.inst, node_idx) ? new_node[:inst] : UNDEF
706742
new_node_type = isassigned(new_nodes.type, node_idx) ? new_node[:type] : UNDEF
@@ -877,6 +913,9 @@ function show_ir(io::IO, compact::IncrementalCompact, config::IRShowConfig=defau
877913
while pop_new_node!(input_idx) !== nothing
878914
count += 1
879915
end
916+
while pop_new_node!(input_idx; attach_after=true) !== nothing
917+
count += 1
918+
end
880919
end
881920

882921
result_bb = result_bbs[compact.active_result_bb]
@@ -918,6 +957,13 @@ function show_ir(io::IO, compact::IncrementalCompact, config::IRShowConfig=defau
918957
pop_new_node! = new_nodes_iter(compact.ir, compact.new_nodes_idx)
919958
maxssaid = length(compact.ir.stmts) + Core.Compiler.length(compact.ir.new_nodes)
920959
let io = IOContext(io, :maxssaid=>maxssaid)
960+
# first show any new nodes to be attached after the last compacted statement
961+
if compact.idx > 1
962+
show_ir_stmt(io, compact.ir, compact.idx-1, config, used_uncompacted,
963+
uncompacted_cfg, bb_idx; pop_new_node!, only_after=true)
964+
end
965+
966+
# then show the actual uncompacted IR
921967
show_ir_stmts(io, compact.ir, compact.idx:length(stmts), config, used_uncompacted,
922968
uncompacted_cfg, bb_idx; pop_new_node!)
923969
end

test/show.jl

Lines changed: 60 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2036,21 +2036,17 @@ let src = code_typed(my_fun28173, (Int,), debuginfo=:source)[1][1]
20362036
lines2 = split(repr(ir), '\n')
20372037
@test all(isspace, pop!(lines2))
20382038
@test popfirst!(lines2) == "2 1 ── $(QuoteNode(1))"
2039-
@test popfirst!(lines2) == "$(QuoteNode(2))" # TODO: this should print after the next statement
20402039
let line1 = popfirst!(lines1)
20412040
line2 = popfirst!(lines2)
20422041
@test startswith(line1, "2 1 ── ")
20432042
@test startswith(line2, "")
20442043
@test line2[12:end] == line2[12:end]
20452044
end
2046-
let line1 = pop!(lines1)
2047-
line2 = pop!(lines2)
2048-
@test startswith(line1, "17 ")
2049-
@test startswith(line2, " ")
2050-
@test line1[3:end] == line2[3:end]
2051-
end
2052-
@test pop!(lines2) == "\$(QuoteNode(4))"
2053-
@test pop!(lines2) == "17 │ \$(QuoteNode(3))" # TODO: this should print after the next statement
2045+
@test popfirst!(lines2) == "$(QuoteNode(2))"
2046+
@test pop!(lines2) == " └─── \$(QuoteNode(4))"
2047+
@test pop!(lines1) == "17 └─── return %18"
2048+
@test pop!(lines2) == " │ return %18"
2049+
@test pop!(lines2) == "17 │ \$(QuoteNode(3))"
20542050
@test lines1 == lines2
20552051

20562052
# verbose linetable
@@ -2530,3 +2526,58 @@ end
25302526
@test contains(str, r"CFG with \d+ blocks")
25312527
@test contains(str, r"bb 1 \(stmt.+\) → bb.*")
25322528
end
2529+
2530+
@testset "IncrementalCompact: correctly display attach-after nodes" begin
2531+
# set some IR
2532+
function foo(i)
2533+
j = i+42
2534+
return j
2535+
end
2536+
ir = only(Base.code_ircode(foo, (Int,)))[1]
2537+
2538+
# insert a bunch of nodes, inserting both before and after instruction 1
2539+
inst = Core.Compiler.NewInstruction(Expr(:call, :identity, 1), Int)
2540+
Core.Compiler.insert_node!(ir, 1, inst)
2541+
inst = Core.Compiler.NewInstruction(Expr(:call, :identity, 2), Int)
2542+
Core.Compiler.insert_node!(ir, 1, inst)
2543+
inst = Core.Compiler.NewInstruction(Expr(:call, :identity, 3), Int)
2544+
Core.Compiler.insert_node!(ir, 1, inst, true)
2545+
inst = Core.Compiler.NewInstruction(Expr(:call, :identity, 4), Int)
2546+
Core.Compiler.insert_node!(ir, 1, inst, true)
2547+
2548+
# at every point we should be able to observe these instructions (in order)
2549+
function verify_display(ir)
2550+
str = sprint(io->show(io, ir))
2551+
lines = split(str, '\n')
2552+
patterns = ["identity(1)",
2553+
"identity(2)",
2554+
"add_int",
2555+
"identity(3)",
2556+
"identity(4)",
2557+
"return"]
2558+
line_idx = 1
2559+
pattern_idx = 1
2560+
while pattern_idx <= length(patterns) && line_idx <= length(lines)
2561+
# we test pattern-per-pattern, in order,
2562+
# so that we skip e.g. the compaction boundary
2563+
if contains(lines[line_idx], patterns[pattern_idx])
2564+
pattern_idx += 1
2565+
end
2566+
line_idx += 1
2567+
end
2568+
@test pattern_idx > length(patterns)
2569+
end
2570+
verify_display(ir)
2571+
2572+
compact = Core.Compiler.IncrementalCompact(ir)
2573+
verify_display(compact)
2574+
2575+
state = Core.Compiler.iterate(compact)
2576+
while state !== nothing
2577+
verify_display(compact)
2578+
state = Core.Compiler.iterate(compact, state[2])
2579+
end
2580+
2581+
ir = Core.Compiler.complete(compact)
2582+
verify_display(ir)
2583+
end

0 commit comments

Comments
 (0)