Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 42 additions & 30 deletions Compiler/src/ssair/passes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -361,20 +361,25 @@ function already_inserted(compact::IncrementalCompact, old::OldSSAValue)
already_inserted_ssa(compact, compact.idx-1)(0, old)
end

function already_inserted_ssa(compact::IncrementalCompact, processed_idx::Int)
return function did_already_insert(phi_arg::Int, old::OldSSAValue)
id = old.id
if id <= length(compact.ir.stmts)
return id <= processed_idx
end
id -= length(compact.ir.stmts)
if id <= length(compact.ir.new_nodes)
return did_already_insert(phi_arg, OldSSAValue(compact.ir.new_nodes.info[id].pos))
end
id -= length(compact.ir.new_nodes)
@assert id <= length(compact.pending_nodes)
return !(id in compact.pending_perm)
function _already_inserted_ssa(compact::IncrementalCompact, processed_idx::Int,
phi_arg::Int, old::OldSSAValue)
id = old.id
if id <= length(compact.ir.stmts)
return id <= processed_idx
end
id -= length(compact.ir.stmts)
if id <= length(compact.ir.new_nodes)
return _already_inserted_ssa(compact, processed_idx, phi_arg,
OldSSAValue(compact.ir.new_nodes.info[id].pos))
end
id -= length(compact.ir.new_nodes)
@assert id <= length(compact.pending_nodes)
return !(id in compact.pending_perm)
end

function already_inserted_ssa(compact::IncrementalCompact, processed_idx::Int)
return (phi_arg::Int, old::OldSSAValue) ->
_already_inserted_ssa(compact, processed_idx, phi_arg, old)
end

function is_pending(compact::IncrementalCompact, old::OldSSAValue)
Expand Down Expand Up @@ -1647,6 +1652,25 @@ function reachable_blocks(cfg::CFG, from_bb::Int, to_bb::Int)
return visited
end

function _update_finalizer_insert!(ir::IRCode, lazypostdomtree::LazyPostDomtree,
finalizer_idx::Int, insert_bb::Int,
insert_idx::Union{Int,Nothing}, x::Union{Int,SSAUse})
defuse_idx = x isa SSAUse ? x.idx : x
defuse_idx == finalizer_idx && return insert_bb, insert_idx
defuse_bb = block_for_inst(ir, defuse_idx)
new_insert_bb = nearest_common_dominator(get!(lazypostdomtree),
insert_bb, defuse_bb)
if new_insert_bb == insert_bb && insert_idx !== nothing
insert_idx = max(insert_idx::Int, defuse_idx)
elseif new_insert_bb == defuse_bb
insert_idx = defuse_idx
else
insert_idx = nothing
end
insert_bb = new_insert_bb
return insert_bb, insert_idx
end

function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int, defuse::SSADefUse,
inlining::InliningState, lazydomtree::LazyDomtree,
lazypostdomtree::LazyPostDomtree, @nospecialize(info::CallInfo))
Expand All @@ -1668,24 +1692,12 @@ function try_resolve_finalizer!(ir::IRCode, alloc_idx::Int, finalizer_idx::Int,
# Check #2: The insertion block for the finalizer is the post-dominator of all uses
insert_bb::Int = finalizer_bb
insert_idx::Union{Int,Nothing} = finalizer_idx
function note_defuse!(x::Union{Int,SSAUse})
defuse_idx = x isa SSAUse ? x.idx : x
defuse_idx == finalizer_idx && return nothing
defuse_bb = block_for_inst(ir, defuse_idx)
new_insert_bb = nearest_common_dominator(get!(lazypostdomtree),
insert_bb, defuse_bb)
if new_insert_bb == insert_bb && insert_idx !== nothing
insert_idx = max(insert_idx::Int, defuse_idx)
elseif new_insert_bb == defuse_bb
insert_idx = defuse_idx
else
insert_idx = nothing
end
insert_bb = new_insert_bb
nothing
for x in defuse.uses
insert_bb, insert_idx = _update_finalizer_insert!(ir, lazypostdomtree, finalizer_idx, insert_bb, insert_idx, x)
end
for x in defuse.defs
insert_bb, insert_idx = _update_finalizer_insert!(ir, lazypostdomtree, finalizer_idx, insert_bb, insert_idx, x)
end
foreach(note_defuse!, defuse.uses)
foreach(note_defuse!, defuse.defs)
insert_bb != 0 || return nothing # verify post-dominator of all uses exists

if !OptimizationParams(inlining.interp).assume_fatal_throw
Expand Down
165 changes: 85 additions & 80 deletions Compiler/src/ssair/show.jl
Original file line number Diff line number Diff line change
Expand Up @@ -534,37 +534,33 @@ function DILineInfoPrinter(debuginfo, def, showtypes::Bool=false)
started::Bool = false
if !update_line_only && showtypes && !isa(frame.method, Symbol) && nctx != 1
print(io, linestart)
with_output_color(linecolor, io) do io
print(io, indent("│"))
print(io, "┌ invoke ", frame.method)
println(io)
end
printstyled(io, indent("│"), color=linecolor)
printstyled(io, "┌ invoke ", frame.method, color=linecolor)
println(io)
started = true
end
print(io, linestart)
with_output_color(linecolor, io) do io
print(io, indent("│"))
push!(context, frame)
if update_line_only
update_line_only = false
else
context_depth[] += 1
nctx != 1 && print(io, started ? "│" : "┌")
end
print(io, " @ ", frame.file)
if frame.line != typemax(frame.line) && frame.line != 0
print(io, ":", frame.line)
end
print(io, " within `", method_name(frame), "`")
if collapse
method = method_name(frame)
while nctx < nframes
frame = DI[nframes - nctx]
method_name(frame) === method || break
nctx += 1
push!(context, frame)
print(io, " @ ", frame.file, ":", frame.line)
end
printstyled(io, indent("│"), color=linecolor)
push!(context, frame)
if update_line_only
update_line_only = false
else
context_depth[] += 1
nctx != 1 && printstyled(io, started ? "│" : "┌", color=linecolor)
end
printstyled(io, " @ ", frame.file, color=linecolor)
if frame.line != typemax(frame.line) && frame.line != 0
printstyled(io, ":", frame.line, color=linecolor)
end
printstyled(io, " within `", method_name(frame), "`", color=linecolor)
if collapse
method = method_name(frame)
while nctx < nframes
frame = DI[nframes - nctx]
method_name(frame) === method || break
nctx += 1
push!(context, frame)
printstyled(io, " @ ", frame.file, ":", frame.line, color=linecolor)
end
end
println(io)
Expand Down Expand Up @@ -681,6 +677,60 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
sptypes, used, cfg, bb_idx; pop_new_node!, only_after, config.bb_color, config.label_dynamic_calls)
end

function _print_ir_indentation(io::IO, cfg::CFG, bb_idx::Int, max_bb_idx_size::Int, bb_color,
line_info_preprinter, idx::Int, i::Int; final::Bool=true)
# Compute BB guard rail
if bb_idx > length(cfg.blocks)
# If invariants are violated, print a special leader
linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket
inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0)
printstyled(io, "!!! ", "─"^max_bb_idx_size, color=bb_color)
else
bbrange = cfg.blocks[bb_idx].stmts
# Print line info update
linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=bb_color), context=io)
linestart *= " "^max_bb_idx_size
# idx == 0 means only indentation is printed, so we don't print linfos
# multiple times if the are new nodes
inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0)

if i == 1 && idx == first(bbrange)
bb_idx_str = string(bb_idx)
bb_pad = max_bb_idx_size - length(bb_idx_str)
bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄"
printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=bb_color)
elseif final && idx == last(bbrange) # print separator
printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=bb_color)
else
printstyled(io, "│ ", " "^max_bb_idx_size, color=bb_color)
end
end
print(io, inlining_indent, " ")
return nothing
end

function _print_ir_new_node(io::IO, node, code, sptypes::Vector{VarState}, used::BitSet, maxlength_idx::Int,
label_dynamic_calls::Bool, line_info_postprinter, cfg::CFG, bb_idx::Int,
max_bb_idx_size::Int, bb_color, line_info_preprinter, idx::Int, i::Int; final::Bool=true)
_print_ir_indentation(io, cfg, bb_idx, max_bb_idx_size, bb_color, line_info_preprinter, idx, i; final)

node_idx, new_node_inst, new_node_type = node
@assert new_node_inst !== UNDEF # we filtered these out earlier
show_type = should_print_ssa_type(new_node_inst)
with_output_color(:green, io) do io′
print_stmt(io′, node_idx, new_node_inst, code, sptypes, used, maxlength_idx, false, show_type, label_dynamic_calls)
end

if new_node_type === UNDEF
# Try to be robust against errors
printstyled(io, "::#UNDEF", color=:red)
else
line_info_postprinter(io; type = new_node_type, used = node_idx in used, show_type, idx = node_idx)
end
println(io)
return nothing
end

function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact}, idx::Int, line_info_preprinter, line_info_postprinter,
sptypes::Vector{VarState}, used::BitSet, cfg::CFG, bb_idx::Int; pop_new_node! = Returns(nothing), only_after::Bool=false,
bb_color=:light_black, label_dynamic_calls::Bool=true)
Expand All @@ -703,58 +753,11 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
end

i = 1
function print_indentation(final::Bool=true)
# Compute BB guard rail
if bb_idx > length(cfg.blocks)
# If invariants are violated, print a special leader
linestart = " "^(max_bb_idx_size + 2) # not inside a basic block bracket
inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0)
printstyled(io, "!!! ", "─"^max_bb_idx_size, color=bb_color)
else
bbrange = cfg.blocks[bb_idx].stmts
# Print line info update
linestart = idx == first(bbrange) ? " " : sprint(io -> printstyled(io, "│ ", color=bb_color), context=io)
linestart *= " "^max_bb_idx_size
# idx == 0 means only indentation is printed, so we don't print linfos
# multiple times if the are new nodes
inlining_indent = line_info_preprinter(io, linestart, i == 1 ? idx : 0)

if i == 1 && idx == first(bbrange)
bb_idx_str = string(bb_idx)
bb_pad = max_bb_idx_size - length(bb_idx_str)
bb_type = length(cfg.blocks[bb_idx].preds) <= 1 ? "─" : "┄"
printstyled(io, bb_idx_str, " ", bb_type, "─"^bb_pad, color=bb_color)
elseif final && idx == last(bbrange) # print separator
printstyled(io, "└", "─"^(1 + max_bb_idx_size), color=bb_color)
else
printstyled(io, "│ ", " "^max_bb_idx_size, color=bb_color)
end
end
print(io, inlining_indent, " ")
end

# first, print new nodes that are to be inserted before the current statement
function print_new_node(node; final::Bool=true)
print_indentation(final)

node_idx, new_node_inst, new_node_type = node
@assert new_node_inst !== UNDEF # we filtered these out earlier
show_type = should_print_ssa_type(new_node_inst)
let maxlength_idx=maxlength_idx, show_type=show_type
with_output_color(:green, io) do io′
print_stmt(io′, node_idx, new_node_inst, code, sptypes, used, maxlength_idx, false, show_type, label_dynamic_calls)
end
end

if new_node_type === UNDEF # try to be robust against errors
printstyled(io, "::#UNDEF", color=:red)
else
line_info_postprinter(io; type = new_node_type, used = node_idx in used, show_type, idx = node_idx)
end
println(io)
end
while (next = pop_new_node!(idx)) !== nothing
only_after || print_new_node(next; final=false)
only_after || _print_ir_new_node(io, next, code, sptypes, used, maxlength_idx, label_dynamic_calls,
line_info_postprinter, cfg, bb_idx, max_bb_idx_size, bb_color,
line_info_preprinter, idx, i; final=false)
i += 1
end

Expand All @@ -766,7 +769,7 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},
# FIXME: `only_after` is hack so that we can call this function to print uncompacted
# attach-after nodes when the current node has already been compated already
if !only_after
print_indentation(next===nothing)
_print_ir_indentation(io, cfg, bb_idx, max_bb_idx_size, bb_color, line_info_preprinter, idx, i; final=next===nothing)
if code isa CodeInfo
stmt = statement_indices_to_labels(stmt, cfg)
end
Expand All @@ -786,7 +789,9 @@ function show_ir_stmt(io::IO, code::Union{IRCode, CodeInfo, IncrementalCompact},

# finally, print new nodes that are to be inserted after the current statement
while next !== nothing
print_new_node(next)
_print_ir_new_node(io, next, code, sptypes, used, maxlength_idx, label_dynamic_calls,
line_info_postprinter, cfg, bb_idx, max_bb_idx_size, bb_color,
line_info_preprinter, idx, i)
i += 1
next = pop_new_node!(idx; attach_after=true)
end
Expand Down
63 changes: 34 additions & 29 deletions Compiler/src/typeinfer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,39 @@ function codeinst_as_edge(interp::AbstractInterpreter, sv::InferenceState, @nosp
return ci
end

function _schedule_edge_infer_task!(caller::AbsIntState, frame::InferenceState, result::InferenceResult,
method::Method, edge_ci::Union{Nothing,CodeInstance},
edgecycle::Bool, edgelimited::Bool)
mresult = Future{MethodCallResult}()
push!(caller.tasks, function get_infer_result(interp, caller)
update_valid_age!(caller, get_inference_world(interp), frame.valid_worlds)
isinferred = is_inferred(frame)
effects = nothing
edge = nothing
call_result = nothing
if isinferred
edge = result.ci
if edge_ci isa CodeInstance && codeinst_edges_sub(edge_ci, edge.min_world, edge.max_world, edge.edges)
edge = edge_ci # override the edge for tracking invalidation
end
result.ci_as_edge = edge # override the edge for tracking purposes
effects = result.ipo_effects # effects are adjusted already within `finish` for ipo_effects
call_result = result
else
effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method)
add_cycle_backedge!(caller, frame)
end
bestguess = frame.bestguess
exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
# propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
# note that this result is cached globally exclusively, so we can use this local result destructively
mresult[] = MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects,
edge, edgecycle, edgelimited, call_result)
return true
end)
return mresult
end

# compute (and cache) an inferred AST and return the current best estimate of the result type
function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize(atype), sparams::SimpleVector, caller::AbsIntState, edgecycle::Bool, edgelimited::Bool)
mi = specialize_method(method, atype, sparams)
Expand Down Expand Up @@ -1204,35 +1237,7 @@ function typeinf_edge(interp::AbstractInterpreter, method::Method, @nospecialize
assign_parentchild!(frame, caller)
# the actual inference task for this edge is going to be scheduled within `typeinf_local` via the callstack queue
# while splitting off the rest of the work for this caller into a separate workq thunk
let mresult = Future{MethodCallResult}()
push!(caller.tasks, function get_infer_result(interp, caller)
update_valid_age!(caller, get_inference_world(interp), frame.valid_worlds)
local isinferred = is_inferred(frame)
local effects
local edge = nothing
local call_result = nothing
if isinferred
edge = result.ci
if edge_ci isa CodeInstance && codeinst_edges_sub(edge_ci, edge.min_world, edge.max_world, edge.edges)
edge = edge_ci # override the edge for tracking invalidation
end
result.ci_as_edge = edge # override the edge for tracking purposes
effects = result.ipo_effects # effects are adjusted already within `finish` for ipo_effects
call_result = result
else
effects = adjust_effects(effects_for_cycle(frame.ipo_effects), method)
add_cycle_backedge!(caller, frame)
end
local bestguess = frame.bestguess
local exc_bestguess = refine_exception_type(frame.exc_bestguess, effects)
# propagate newly inferred source to the inliner, allowing efficient inlining w/o deserialization:
# note that this result is cached globally exclusively, so we can use this local result destructively
mresult[] = MethodCallResult(interp, caller, method, bestguess, exc_bestguess, effects,
edge, edgecycle, edgelimited, call_result)
return true
end)
return mresult
end
return _schedule_edge_infer_task!(caller, frame, result, method, edge_ci, edgecycle, edgelimited)
elseif frame === true
# unresolvable cycle
add_remark!(interp, caller, "[typeinf_edge] Unresolvable cycle")
Expand Down
Loading